2017-05-31 121 views
1

我的問題是,在短,是否給予Java的子類或實現泛型類型相同的邊界

interface Intf<T extends Bound> {} 
class Clss<T extends Bound> {} 

是有沒有辦法繼承/實現不是重複綁定的類型他們的其他:

class Impl<T extends Bound> implements Intf<T> {} 
class Subc<T extends Bound> extends Clss<T> {} 

我不是抱怨需要重複extends Bound,我甚至理解編譯器邏輯,但我想知道這是否真的是這樣。因爲在兩個地方使用<T>在語義上對我來說足夠了。

+1

不。有很多可能性可以進入'''',而不僅僅是'T'。儘管'T'是一個非常普遍的例子,但是建立某種類型的arg暗示的推理規則並不值得使語言複雜化;最好明確地說明你想要的內容。 – ZhongYu

+0

作爲一個簡單的例子,如果你有'Impl 實現Intf ',會發生什麼? –

回答

1

是的,就是這樣。爲了大家的緣故,這裏是完整的解釋。

這裏就是你們的榜樣的另一個版本(想象整型可擴展)

interface Intf<T extends Number> {} 
class Clss<T extends Number> {} 
class Impl<T extends Integer> implements Intf<T> {} 
class Subc<T extends Integer> extends Clss<T> {} 
class ImplSubc<T extends Integer> extends Clss<T> implements Intf<T> {} 

<T extends Integer>是初始化標籤,以及進一步<T>標籤基本上都是「傳遞下去」的引用。這兩個標籤在視覺上是相同的,但它們是不同的東西。由於沿標記通需要別的東西來初始化請注意以下內容是非法的

class Subc<T extends Integer> extends Clss<T extends Integer> {} 

。所以你可以知道哪些是因爲initialize標籤最終會傳遞給它,而傳遞標籤是根據初始化標籤設置的內容傳遞(或返回)的東西。所以,如果你只有以下幾種:

class Subc<T> extends Clss<T> {} 

編譯單元說:「允許它被設置爲任何東西,並將其傳遞給Clss」。由於您可以將初始化標記傳遞給多個傳遞標記,每個傳遞標記都可能具有不同的邊界,因此需要明確設置初始化標記NEEDS以確保傳遞的<T>確實與您傳遞的所有內容都兼容。雖然這在技術上可以推斷出來,但它昂貴,複雜,並要求可怕的生產破壞錯誤,所以編譯器只會迫使你知道你在做什麼。

更新:
這裏是一個例子,說明如果讓編譯器決定你的代碼是什麼,一個bug如何進入生產。假設編譯器並推斷限制,如果

class Clss<T extends Number> {} 
class Subc<T> extends Clss<T> {} 

成爲

class Clss<T extends BigDecimal> {} 
class Subc<T> extends Clss<T> {} 

沒有extends Integer集上SUBC,讓它可以推斷,這種變化將彙編(如果這是根據在什麼使用它作爲一個獨立的API),並且可能仍然在工作,直到它沒有。因爲你說你明確想要使用Integer或Number,所以現在可以在編譯時確定更改Clss的類型是安全的還是壞的更改,而不是運行時。 Java是安全第一類型語言,這意味着代碼穩定性/可靠性檢測是其特性的核心價值。所以如果有疑問,Java會抱怨。

+0

你的解釋對於爲什麼是這種方式是有意義的,但我沒有得到複雜的推論部分。當你用'Clss '傳遞'T'時,你會說'Subc'中出現'T',它指的是'Clss'的類型參數。編譯器知道不會比接受'Clss'中設置的'extends Number'更少的限制。你能否詳細說明你預見的問題? – entonio

+0

@entonio我擴展了答案。新位有幫助嗎? – Tezra

+0

這是一個很好的例子。我仍然說,避免使用的可能錯誤的類型在語言的其他地方不是強制執行的:在任何時候,您都可以在一個地方引入更改,這些更改會在其他地方以靜默方式破壞代碼。但這是一個很好的例子,所以我接受了答案。 – entonio

1

請注意,雖然Foo<T [constraints]> implements Bar<T>模式非常常見,但它只有一種可能性。這裏有一些人(非詳盡):

Foo<T extends DerivedBound>  implements Bar<T> 
Foo<T>        implements Bar<DerivedBound> 
Foo<T>        implements Bar<ParameterisedBound<T>> 
Foo<T, U extends Bound>   implements Bar<U> 
Foo<T extends BarBound & BazBound> implements Bar<T>, Baz<T> 

所以這是在其指定的約束兩次沒有冗餘 - 信息你提供兩個獨立的部件來編譯。

從理論上講,語言設計者可以爲Java添加一個特例,以節省一些字符。但假設他們甚至考慮過這個選擇,我想他們認爲額外的複雜性不是一個好的折衷。

+0

目的不是爲了保存字符,而是避免重複自由形式的信息,這總是一種反模式。 @Tezra確實給出了一個合理的例子,說明爲什麼,它可能被看作是一種「複式簿記」,而不是冗餘的東西,這是可以接受的。 – entonio

+1

@entonio - 但重點在於沒有重複的信息 - 「酒吧」有一個界限,而且你碰巧使用了與「Foo」相同的界限 - 這是兩個獨立的信息。我相信,降低語言/編譯器複雜性成爲這裏的主要因素的可能性更大。 –

+0

有 - 通過使用相同的類型參數('T'),您說用於參數化Sub的類型與用於參數化「Super」的類型相同(它們不在不同的名稱空間中)。由於'Super'已經有了限制,'Sub'必須符合它們。如果你使用不同的參數,比如說'T'和'U',那將是不同的。那麼你將獨立地對它們進行參數化,是的,編譯器將沒有義務推斷任何東西。 – entonio