[英]How to make two classes which both have method with the same name, signature and return type behave like they would implement the same interface
我将以一个例子解释我的问题。
我有两个类NumberGeneratorApple
和NumberGeneratorOrange
。 它们都具有相同签名的方法,并返回类型public Integer getNumber()
。 问题是他们没有实现相同的界面,尽管它是合乎逻辑的。 我可以使用此方法创建接口并修改这些类来实现它,但我不想这样做。 这就是原因。 这些类是自动生成的,例如xml。 在实际例子中,有许多这样的类。 我们必须不时地生成它们并覆盖旧的。 我不想手动更改每个类以在每一代之后实现接口。 此外,对于在同一项目中工作的其他人,甚至在一段时间后对我来说,也可能不应该这样做。
这是第一堂课:
public class NumberGeneratorApple {
public Integer getNumber(){
return 9;
}
}
第二个:
public class NumberGeneratorOrange {
public Integer getNumber(){
return 19;
}
}
虽然他们没有实现我需要以通用方式使用它们的相同接口。 我只是想做这样的事情:
public class ClassThatOperates<T extends NumberGeneratorInterface> {
T generator;
public ClassThatOperates(T generator) {
this.generator = generator;
}
public void doSomeOperation(){
//Obviously it won't work for NumberGeneratorApple and NumberGeneratorOrange
// because they do not implement NumberGeneratorInterface
//And if I change "<T extends NumberGeneratorInterface>" to "<T>"
// I cannot write method call "generator.getNumber()"
System.out.print("The number with constant is: ");
System.out.println(generator.getNumber() + 5);
}
}
这是界面(我知道公共在那里没有必要,它默认存在。我只想强调它):
public interface NumberGeneratorInterface {
public Integer getNumber();
}
正如你所看到的,不可能做到这一点,因为NumberGeneratorApple
和NumberGeneratorOrange
都没有实现NumberGeneratorInterface
。 然而我想出了一些解决方案,但是按照我的直觉,我觉得它很差。 我做了包装类:
public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
}
和
public class NumberGeneratorOrangeWrapper extends NumberGeneratorOrange implements NumberGeneratorInterface {
}
这有点棘手。 一开始可能并不明显,但是当你在其中一个类的对象上调用getNumber()时,你实际上就是这样调用的:
@Override
public Integer getNumber() {
return super.getNumber();
}
现在我可以这样称呼它:
public class Main {
public static void main(String[] args) {
ClassThatOperates<NumberGeneratorAppleWrapper> classThatOperatesApple = new ClassThatOperates<>(new NumberGeneratorAppleWrapper());
ClassThatOperates<NumberGeneratorOrangeWrapper> classThatOperatesOrange = new ClassThatOperates<>(new NumberGeneratorOrangeWrapper());
classThatOperatesApple.doSomeOperation();
classThatOperatesOrange.doSomeOperation();
}
}
我得到以下输出:
The number with constant is: 14
The number with constant is: 24
这种方法的优点而不是手动为每个生成的类implements NumberGeneratorInterface
是我们不必在每一代之后重复相同的工作(它会覆盖旧类)。 我们只需要在生成一些新的附加类时添加一个新的包装器。
我知道在这种情况下我可以在ClassThatOperates
删除泛型并只声明NumberGeneratorInterface generator;
没有<T extends...>
等等(我甚至应该)所以代码会更简单,但我想让这个例子与我在一些真实项目中发现的非常相似。 我写道:我有,我做了,我来了等等,但事实上它是基于我在某个项目中发现的现有代码。
有我的问题:
1.有更好的解决方案吗?
这个解决方案是否是一种所谓的“不良品味”?
如果我必须使用这样的解决方案,那么我的整个方法可能是错的?
4.如果没有更好的解决方案,即使整个方法都是错误的,可以在此代码中改进(包括删除泛型)?
- 有更好的解决方案吗?
你的解决方案对我来说很好。 您已使用适配器模式 ,它使用现有功能来符合其他不相关的接口。 在不更改NumberGeneratorApple
和NumberGeneratorOrange
,您已将这些类的功能调整为NumberGeneratorInterface
。
更一般地说,您可以将具有不同签名的现有方法适用于界面,例如,如果您有
public class NumberGeneratorApple {
public Integer getAppleNumber(){
return 9;
}
}
然后,您的适配器类将在实现接口时显式调用getAppleNumber
。
public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
@Override
public Integer getNumber() {
return getAppleNumber();
}
}
- 这个解决方案是所谓的“坏味道”吗?
这种解决方案没有不好的味道。 适配器模式是一种成熟的软件设计模式。
- 如果我必须使用这样的解决方案,也许我的整个方法是错的?
如果您无法更改现有的类(如NumberGeneratorApple
,则可以使用适配器模式。 如果你可以改变它们,那么让这些类直接实现必要的接口。
public class NumberGeneratorApple implements NumberGeneratorInterface {
@Override
public Integer getNumber() {
return 9;
}
}
或者,如果方法签名不同:
public class NumberGeneratorApple implements NumberGeneratorInterface {
public Integer getAppleNumber() {
return 9;
}
@Override
public Integer getNumber() {
return getAppleNumber();
}
}
- 如果没有更好的解决方案,即使整个方法都是错误的,可以在这段代码中改进(包括摆脱泛型)?
如果您的类(如NumberGeneratorApple
确实只有一个方法,并且不仅仅是为了解决此问题而使用多个方法简化更复杂的类,那么您可以使用方法引用作为另一个答案。 可以将方法引用键入为Supplier<Integer>
,而不是声明自己的接口NumberGeneratorInterface
。
public class ClassThatOperates {
Supplier<Integer> generator;
public ClassThatOperates(Supplier<Integer> generator) {
this.generator = generator;
}
public void doSomeOperation(){
System.out.print("The number with constant is: ");
System.out.println(generator.get() + 5);
}
}
然后你可以用它作为:
NumberGeneratorOrange ngo = new NumberGeneratorOrange();
ClassThatOperates cto = new ClassThatOperates(ngo::getNumber);
cto.doSomeOperation();
使用java8,您可以实例化一个自定义类
NumberGeneratorApple apple = new NumberGeneratorApple();
并从中创建一个通用供应商:
Supplier<Integer> numberGenerator = apple::getNumber;
然后你可以传递它或做任何你需要的东西:
Integer number = numberGenerator.get();
externalObject.execute(numberGenerator);
那声音怎么样?
考虑:
class ClassThatOperates
{
final Supplier<Integer> m_generator;
ClassThatOperates(Supplier<Integer> generator)
{
m_generator = generator;
}
void doSomething()
{
...
m_generator.get();
}
}
NumberGeneratorApple nga = new NumberGeneratorApple();
ClassThatOperates cta = new ClassThatOperates(nga::generate);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.