[英]Java generics error with layered composite pattern
我正在使用泛型來限制復合層次結構中的子類型,這樣我就可以在層次結構的不同層強制使用不同的類型。
例如,具有(Business:Division:Department:Group:Person)層次結構,其中每個級別的復合節點僅接受正確類型的子節點(層次結構中的下一個較低級別)。 因此,我將在實例化的每個級別類型中使用泛型構造復合中的級別,以僅接受來自下一個較低級別的節點。
但是我對泛型有誤,並且不確定它是否表示設計錯誤,或者只是java Generics不能為我做的事情。 這個想法似乎有效,我只想限制add()方法接受的類型,只接受層次結構中特定下一級的子類型來強制結構級別。 由於我只是在每個階段降低類型的上限,所以發送到列表的所有消息仍然有效。
典型GOF-Composite模式中的頂級節點:
public abstract class Business { ... }
復合節點層次結構頂級:
public abstract class Group extends Business {
protected List<Business> subs; // composite links
public Group add(Business comp) {
subs.add(comp);
return this;
}
復合層次結構中的后續級別:
public class Area<T extends Business> extends Group {
protected List<Business> subs; // composite links
public Area(String title){
super(title);
subs = new ArrayList<Business>();
}
@Override
public Group add(T comp) { // ** Error Here **
super.add(comp);
return this;
}
錯誤是:
Multiple markers at this line - The method add(T) of type Area<T> must override a superclass method
- 名稱沖突:類型為Area的add(T)方法與Group類型的add(Business)具有相同的擦除功能但不覆蓋它
我嘗試了一個變體,我給了Group :: add()方法一個類似的類型,所以他們會有相同的類型簽名,
public group add(T comp){...}
但是同樣失敗了:
Multiple markers at this line
- 類型的方法add(T)必須覆蓋超類方法 - 覆蓋labs.composite.company.Group.add - 名稱沖突:類型為Area的方法add(T)與add(T)類型具有相同的刪除分組,但不會覆蓋它
我在這里遺漏了什么嗎??? TIA
PS:實際上我認為這種泛型的使用並不能完全符合我的要求(即使它確實有效!),因為我不僅希望改變每個級別的上限,而且需要特定的單個級別。類型層次結構,而不是任何協變參數類型。 我真的想要這樣的東西,“ 對於任何類型的T,它是Composite的子類型,在add()方法中只接受那種類型的對象 ”,我認為相反,泛型說“ 接受任何對象作為參數是Composite的子類型 “。 我認為這是不可能的,因為Java中的參數是協變的,並且LSP將始終允許使用子類型。
如果你的問題是為什么錯誤,原因很簡單: T
的Area
可以的無限Business
的子類,而Business
在Group
是一個非常具體的類。 兩種方法都不一樣。 編譯器在執行類型檢查時主要使用完整類型(即使它還考慮了某種警告和特殊情況的刪除)。 這是仿制葯的全部要點。 否則,最好還原為pre-generics代碼樣式(仍然支持)。
如果沒有,那么我不明白這個問題。 但是,我還要補充幾點意見:
1)繼承描述“是一種”關系,而不是“有”或“由......組成”的關系。 在代碼中,您說Group
是一種Business
,而Area
是一種Group
。 對我而言,認為Group
屬於 Business
(即Business
擁有 Group
)更為自然。 Group
s和Area
s也是如此。 這兩種關系都不是遺傳,而是構成。
2)當在class Group<T extends Business>
定義方法add(T comp)
,在class Area<T extends Business>
定義方法add(T comp)
,你說Group::add
和Area:add
有相同的簽名。 那不對。 組中的通用參數T
完全獨立於區域中的T
為什么? 假設B1
和B2
是Bussiness的子類,但彼此不是。 沒有人可以說Area<B1>:add()
和Group<B2>:add()
具有相同的簽名。 事實上,這種等價的唯一情況是泛型參數是相同的(即Area<B1>
和Group<B1>
)。 當等效性僅在少數特定情況下(代碼未另外描述)時,Java不能認為簽名等效。
3)GOF的復合設計模式不適用於這種情況,因為它不代表層次結構 ,而是我們稱之為“不受限制”的組合。 根據這種模式, aComposite
可以包含differentComponents
Component
,但是這些Component
可以是任何類型的Composite
,無論類層次結構有多高或多低。 你想要的是differentComponents
,它們是最低級的。 不多也不少。
我不記得曾將此案例視為一種設計模式。 可能是因為它太簡單了。 請參閱下一點中的最后一個變體4)。
4)您的代碼應該采用以下形式:
public class Grouping<C,T> {
C curr;
List<T> subs;
public C add(T comp) {
this.subs.add(comp);
return this.curr ;
}
}
public class Business extends Grouping<Business,Group> {
// Grouping::add does not need to be overriden
}
public class Group extends Grouping<Group,Area> {
// Grouping::add does not need to be overriden
}
等等。 如果允許方法add
返回void
而不是C
,則可以刪除所有類中的泛型參數:
public class Grouping<T> {
List<T> subs;
public void add(T comp) {
this.subs.add(comp);
}
}
public class Business extends Grouping<Group> {
// Grouping::add does not need to be overriden
}
public class Group extends Grouping<Area> {
// Grouping::add does not need to be overriden
}
如果你想讓它變得非常簡單(但也許不那么強大):
public class Business {
List<Group> subs;
public Business add(Group comp) {
this.subs.add(comp);
return this ;
}
}
public class Group {
List<Area> subs;
public Group add(Area comp) {
this.subs.add(comp);
return this ;
}
}
這會創建代碼重復(在每個類中add
方法),在更現實的情況下可能會更高(您可能還需要方法count
, list
, retrieve
等)。
覆蓋是指當您擁有相同的簽名時,您的簽名是不同的,因此它不是有效的覆蓋。 你的方法簽名應該是。
public Group add(Business comp)
既然T必須擴展業務,我無法理解為什么這是一個問題,如果它只是擺脫覆蓋注釋。
您可能會混淆覆蓋重載,這是簽名不同但名稱相同的情況。
從技術上講, @Override
注釋沒有任何功能,它只是在那里捕獲程序員錯誤,所以如果它不編譯只是擺脫它
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.