我会用一个例子来解释我的问题。
我有两个类(class) NumberGeneratorApple
和 NumberGeneratorOrange
.它们都有相同签名和返回类型的方法 public Integer getNumber()
.问题是他们没有实现相同的接口(interface),尽管这是合乎逻辑的。我可以使用此方法创建接口(interface)并修改这些类以实现它,但我不想这样做。这就是为什么。这些类是从 xml 自动生成的。在真实的例子中有几十个这样的类。我们必须不时生成它们,它会覆盖旧的。我不想在每一代之后手动更改每个类来实现接口(interface)。此外,对于从事同一项目的其他人,甚至一段时间后的我来说,这可能并不明显。
这是第一个类:
public class NumberGeneratorApple {
public Integer getNumber(){
return 9;
}
}
第二个:
public class NumberGeneratorOrange {
public Integer getNumber(){
return 19;
}
}
虽然它们没有实现相同的接口(interface),但我需要以通用方式使用它们。我只想做这样的事情:
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);
}
}
这是接口(interface)(我知道public不是必需的,默认是有的,我只是想强调一下):
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.有没有更好的解决方案?
2. 这个解决方案是所谓的“坏味道”吗?
3.如果我必须使用这样的解决方案,也许我的整个方法都是错误的?
4. 如果没有更好的解决方案,即使整个方法都是错误的,这段代码有什么可以改进的(包括摆脱泛型)?
最佳答案
- Is there a better solution?
我觉得你的解决方案不错。您已经使用了 Adapter Pattern ,它使用现有功能来符合其他不相关的接口(interface)。不改变NumberGeneratorApple
和 NumberGeneratorOrange
,您已将这些类的功能调整为 NumberGeneratorInterface
.
更一般地说,您可以让具有不同签名的现有方法适应接口(interface),例如如果你有
public class NumberGeneratorApple {
public Integer getAppleNumber(){
return 9;
}
}
然后你的适配器类将显式调用 getAppleNumber
在实现接口(interface)时。
public class NumberGeneratorAppleWrapper extends NumberGeneratorApple implements NumberGeneratorInterface {
@Override
public Integer getNumber() {
return getAppleNumber();
}
}
- Is this solution a so called "bad taste"?
这个解决方案没有留下任何不好的味道。适配器模式是一种成熟的软件设计模式。
- If I have to use such solution maybe my whole approach is wrong?
如果您无法更改现有类,例如 NumberGeneratorApple
,那么适配器模式就是要走的路。如果您可以更改它们,那么只需让这些类直接实现必要的接口(interface)即可。
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();
}
}
- If there is no better solution even if the whole approach is wrong what could be improved in this code (including getting rid of generics)?
如果你的类如NumberGeneratorApple
确实只有一种方法,而不仅仅是为了这个问题的目的使用多种方法简化更复杂的类,那么您可以使用方法引用作为另一个答案所暗示的。而不是声明自己的接口(interface) NumberGeneratorInterface
, 方法引用可以输入为 Supplier<Integer>
.
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();
关于java - 如何使两个具有相同名称、签名和返回类型的方法的类表现得像它们实现相同的接口(interface)一样,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53509223/