[英]Java generics incompatible types while subclassing
雖然從通用 class 類型/正式類型參數T/E
與有效 class 類型/實際類型參數進行子類化,例如Type/String
,但會出現許多組合,並且會混淆使用哪個以及何時使用?
public class SubClass<T> implements SuperIfc<T> <-- It is straight forward to understand
public class SubClass<T> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc<T>
public class SubClass<Type> implements SuperIfc<Type>
public class SubClass<Type> implements SuperIfc
public class SubClass implements SuperIfc<Type>
public class SubClass implements SuperIfc<T> <--- Hope we cannot declare <T> in his case while initialising SubClass.
// Bounded type parameter
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T extends Type> implements SuperIfc<T> <-- Looks <T> at SuperIfc also refers <T extends Type>, and no need to declare it again at SuperIfc.
// Recursive type bound
public class SubClass<T extends Comparable<T>>> implements SuperIfc<T>
public class SubClass<T extends Comparable<T>>> implements SuperIfc<Type>
這樣我incompatible types while subclassing
情況1:
public class Test {
interface TestIfc {
public static <T extends TestIfc> T of(int choice) {
if(choice == 1) {
return new TestImpl(); <-- PROB_1: incompatible type error
} else {
return new SomeOtherTestImpl(); //incompatible type error
}
}
}
static class TestImpl implements TestIfc {}
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc {
//The below method also having same error though with declaration
public T of() {
return new TestImpl(); <-- PROB_2: incompatible type error
}
}
}
Case_1: PROB_1: return type is T extends TestIfc
and TestImpl implements TestIf
那么有什么問題呢?
Case_1:PROB_2:與PROB_1類似,如何在沒有外部鑄造的情況下進行糾正。 請幫忙。
案例_2:
public interface SuperIfc<T> {
public T create(Object label);
}
class Type {
public static Type of(){
return new Type();
}
}
------
public class SubClass<Type> implements SuperIfc<Type>{
@Override
public Type create() {
return Type.of(); <---- PROB_1: cannot resolve method
}
}
-------
public class SubClass<T extends Type> implements SuperIfc<Type>{
@Override
public Type create() {
return Type.of(); <---- PROB_1: is resolved
}
}
SuperIfc<Type> object = new SubClass(); <-- PROB_2 Unchecked assignement warning
SuperIfc<Type> object = new SubClass<TypeImpl>(); <-- PROB_3: bound should extend Type
我想知道如何同時解決 Case_2、PROB_1 和 PROB_2?
如何使用 class 類型為通用超級 class 編寫子類,規則是什么?
在子類化時將泛型T
更改為 class Type
時應該注意什么? 可能是下面和何時使用之間的區別?
public class SubClass<Type> implements SuperIfc<Type> public class SubClass<Type> implements SuperIfc public class SubClass implements SuperIfc<Type> public class SubClass<T extends Type> implements SuperIfc<Type> public class SubClass<T extends Type> implements SuperIfc<T> public class SubClass<T> implements SuperIfc<Type>
在第一個of()
方法中,該方法可以返回任何實現InformationIfc
的類型,但您的方法總是返回一個特定的實現 - InformationImpl
- 這是不可接受的。
例如,如果您有其他一些實現該接口的 class SomeOtherInformationImpl
,則該方法的調用者將被允許編寫:
SomeOtherInformationImpl i = InformationImpl.of();
但您的方法不返回SomeOtherInformationImpl
。
第二種of()
方法與第一種方法具有相同的問題。 如果您使用以下命令實例化 class:
InformationImpl i = new InformationImpl<SomeOtherInformationImpl>();
of()
方法必須返回SomeOtherInformationImpl
,而不是InformationImpl
。
案例一的問題。
PROB_1:返回類型是 T 擴展 TestIfc
為什么你這里有一個通用的? 既然你有我可以做的 static 方法。
TestIfc broken = TestIfc<SomeOtherImplementation>.of(0);
SomeOtherImplementation
不是TestImpl
。 這被設計破壞了。 你真正想要的是。
public static TestIfc of(int choice)
下一個。
static class SomeOtherTestImpl<T extends TestIfc> 實現 TestIfc {
TestIfc
沒有參數化, SomeOtherTestImp
是,但它與您正在實現的接口完全無關。 更不用說,TestIfc 有一個與接口無關of
static 方法。
如果我不得不猜測,我會認為你想要。
interface TestIfc<T>{}
static class TestImpl implements TestIfc<TestImpl> {}
static class SomeOtherTestImpl<T extends TestIfc> implements TestIfc<T>{}
這是我能想到的最好的,因為不清楚你真正想要發生什么。
您對問題 3 的示例
public class SubClass<Type> implements SuperIfc<Type>
這被破壞了,因為SubClass<Type>
將Type
聲明為泛型參數的名稱。 它對類型沒有限制,因此您會得到方法未找到錯誤。
public class SubClass<Type> implements SuperIfc
Broken,創建一個名為Type
的泛型參數,與您的SuperIfc
原始類型版本無關
public SubClass implements SuperIfc<Type>
這很好。
public class SubClass<T extends Type> implements SuperIfc<Type>
public class SubClass<T> implements SuperIfc<Type>
這些都很好,但 T 與 SuperIfc 參數無關,因此您的實現將是。
public Type create(Object label);
第一個通用參數表示您將通過 class 使用的名稱。
這是一個很長的答案,但請通讀它(至少案例 1 和最后,如果您願意,您可以跳過問題 2 的解決方案)
您的問題是編譯器無法證明T
與TestImpl
或SomeOtherTestImpl
相同。 如果還有另一個 class,稱為TestFoo
,實現TestIfc
怎么辦? 然后,如果您將該方法稱為TestIfc.<TestFoo>of(1)
,您會期望 TestFoo 類型的TestFoo
,但您會得到一個TestImpl
,這是錯誤的。 編譯器不希望您這樣做,因此會引發錯誤。 您應該做的只是刪除 generics,如下所示:
public static TestImpl of(int choice) {
if(choice == 1) {
return new TestImpl();
} else {
return new SomeOtherTestImpl();
}
}
然后你可以安全地調用TestIfc.of
。
這基本上是一樣的。 編譯器無法證明SomeOtherTestImpl
的類型參數T
與TestImpl
相同。 如果您有這樣的 object 怎么辦(其中TestFoo
不擴展TestImpl
但實現TestIfc
)?
SomeOtherTestImpl<TestFoo> testImpl = ...;
然后你嘗試像這樣調用of
方法:
TestFoo testFoo = testImpl.of();
你能看出這里的問題嗎? 您of
方法返回一個TestImpl
,但您希望它返回一個TestFoo
因為T
是TestFoo
。 編譯器會阻止您這樣做。 同樣,您應該擺脫 generics:
public TestImpl of() {
return new TestImpl(); //This works now
}
之所以會出現此問題,是因為您將類型參數命名為與 class- Type
相同的名稱。 當您將類型參數的名稱更改為T
時,它將起作用,因為現在Type
是 class 的名稱。 之前,您的類型參數Type
隱藏了 class Type
。
但是,同樣,不需要類型參數,因為您可以這樣做,它會滿足所有約束。
public class SubClass implements SuperIfc<Type> {
@Override
public Type create() {
return Type.of();
}
}
未經檢查的分配是因為您沒有在您提供類型 arguments 給SubClass
時
SuperIfc<Type> object = new SubClass();
這可以通過將Type
顯式賦予SubClass
來解決:
SuperIfc<Type> object = new SubClass<Type>();
或放入空鑽石(我更喜歡)。 第二種方法意味着編譯器可以自行推斷new Subclass<>()
,您的意思是new Subclass<Type>()
。
SuperIfc<Type> object = new SubClass<>();
為什么編譯器抱怨:
該構造函數調用( new Subclass()
)基本上就像調用一個看起來像public SubClass makeNewSubClass() {...}
的方法。 事實上,讓我們用這個替換上面的那個語句,這將導致同樣的警告。
SuperIfc<Type> object = makeNewSubClass();
SubClass
通常采用 1 個類型參數(稱為T
),但在這里,它沒有給出任何類型參數。 這意味着T
可以是任何東西,任何擴展Type
的 class (因為約束SubClass<T extends Type>
)。
你可能會想,如果T
總是成為Type
的子類,它應該可以正常工作,因為上面的 object 的類型是object
SuperIfc<Type>
。 假設有一個像這樣的 class - class TypeFoo extends Type
- 並且方法makeNewSubClass
實際上返回一個 object 類型為SubClass<TypeFoo>
( new SubClass<TypeFoo>()
.)
因此,您希望 object 是SuperIfc<Type>
但它實際上是SubClass<TypeFoo>
。 您可能認為這沒關系,因為畢竟TypeFoo
是Type
的子類,對吧? 好吧,Java 的 generics 實際上是invariant ,這意味着對於編譯器, SubClass<TypeFoo>
不是SuperIfc<Type>
的子類 - 它認為它們是兩種完全不相關的類型。 不要問我——我不知道為什么:)。 甚至SubClass<TypeFoo>
也不被視為與SubClass<Type>
相同或子類!
這就是編譯器發出警告的原因 - 因為原始類型(當你有new SubClass()
而不給出類型參數時你稱之為它)可以代表任何東西。 至於為什么它會發出警告而不是錯誤 - generics 是在 Java 5 中引入的,因此對於在此之前的代碼進行編譯,編譯器只會讓您發出警告。
根據錯誤,您提供的類型參數( TypeImpl
)應該擴展Type
。 這是因為您將SubClass
定義為class SubClass<T extends Type>...
。 在這里,您將TypeImpl
作為T
的參數,因此TypeImpl
必須擴展Type
,所以您需要做的就是放置 do class TypeImpl extends Type
來解決該特定錯誤。
然而,即使你這樣做了,你也會得到一個“不兼容的類型”錯誤,因為正如我在前面談到問題 2 時所說的那樣, SuperIfc<Type>
不被視為SubClass<TypeImpl>
的超類型,即使TypeImpl
擴展了Type
。
你可以這樣做
SuperIfc<TypeImpl> object = new SubClass<TypeImpl>();
或者你可以這樣做
SuperIfc<? extends Type> object = new SubClass<TypeImpl>();
在第二種解決方案中,我們丟失了SuperIfc<T>
中的T
到底是什么的信息; 我們所知道的是它擴展了Type
。 第二個的好處是您可以稍后將任何SubClass
object 重新分配給它(這不適用於第一個版本,因為它只允許SubClass<TypeImpl>
:
SuperIfc<? extends Type> object = new SubClass<TypeImpl>();
object = new SubClass<Type>(); //works great
您似乎想要做的是根據類型參數返回不同類型的對象。 這實際上是不可能的,因為類型參數在運行時會被刪除。 不過,有一些解決方法。
這是使用功能接口的一種方法。 它不涉及鑄造並確保類型安全。
//This is the functional interface. You can also use Supplier here, of course
interface Constructor<T> {
T create();
}
class SubClass<T extends Type> implements SuperIfc<T> {
public Constructor<T> ctor;
public SubClass(Constructor<T> ctor) {
this.ctor = ctor;
}
@Override
public T create(Object label) {
return ctor.create();
}
}
class Type {}
class TypeImpl extends Type{}
通過使用Constructor
object,您將知道返回的Type
object 將始終是正確的類型。 這很容易應用於您現有的代碼 - 您不需要編寫自己的ConstructorImpl
類或任何東西。
您現在可以編寫此代碼了,您需要添加的只是對構造函數 ( Type::new
) 的方法引用。
SuperIfc<Type> object1 = new SubClass<>(Type::new);
SuperIfc<? extends Type> object2 = new SubClass<>(TypeImpl::new);
使用 lambdas,您還可以用稍微冗長的方式編寫它:
SuperIfc<TypeImpl> object = new SubClass<>(() -> new TypeImpl());
---
SuperIfc<? extends Type> object = new SubClass<>(() -> new TypeImpl());
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.