[英]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.