簡體   English   中英

Java generics 子類化時類型不兼容

[英]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
  1. 我想知道如何同時解決 Case_2、PROB_1 和 PROB_2?

  2. 如何使用 class 類型為通用超級 class 編寫子類,規則是什么?

  3. 在子類化時將泛型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 的解決方案)

情況1

問題一:

您的問題是編譯器無法證明TTestImplSomeOtherTestImpl相同。 如果還有另一個 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

問題2:

這基本上是一樣的。 編譯器無法證明SomeOtherTestImpl的類型參數TTestImpl相同。 如果您有這樣的 object 怎么辦(其中TestFoo不擴展TestImpl但實現TestIfc )?

SomeOtherTestImpl<TestFoo> testImpl = ...;

然后你嘗試像這樣調用of方法:

TestFoo testFoo = testImpl.of();

你能看出這里的問題嗎? of方法返回一個TestImpl ,但您希望它返回一個TestFoo因為TTestFoo 編譯器會阻止您這樣做。 同樣,您應該擺脫 generics:

public TestImpl of() {
  return new TestImpl(); //This works now
}



案例二

問題 1

之所以會出現此問題,是因為您將類型參數命名為與 class- Type相同的名稱。 當您將類型參數的名稱更改為T時,它將起作用,因為現在Type是 class 的名稱。 之前,您的類型參數Type隱藏了 class Type

但是,同樣,不需要類型參數,因為您可以這樣做,它會滿足所有約束。

public class SubClass implements SuperIfc<Type> {
    @Override
    public Type create() {
        return Type.of();
    }
}

問題2:

未經檢查的分配是因為您沒有在您提供類型 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> 您可能認為這沒關系,因為畢竟TypeFooType的子類,對吧? 好吧,Java 的 generics 實際上是invariant ,這意味着對於編譯器, SubClass<TypeFoo>不是SuperIfc<Type>的子類 - 它認為它們是兩種完全不相關的類型。 不要問我——我不知道為什么:)。 甚至SubClass<TypeFoo>也不被視為與SubClass<Type>相同或子類!

這就是編譯器發出警告的原因 - 因為原始類型(當你有new SubClass()而不給出類型參數時你稱之為它)可以代表任何東西。 至於為什么它會發出警告而不是錯誤 - generics 是在 Java 5 中引入的,因此對於在此之前的代碼進行編譯,編譯器只會讓您發出警告。

問題3:

根據錯誤,您提供的類型參數( 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

案例 2 的替代解決方案

您似乎想要做的是根據類型參數返回不同類型的對象。 這實際上是不可能的,因為類型參數在運行時會被刪除。 不過,有一些解決方法。

這是使用功能接口的一種方法。 它不涉及鑄造並確保類型安全。

//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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM