簡體   English   中英

嵌套泛型繼承

[英]Nested Generics Inheritance

我有以下課程:

class Field<T> {

  private final Class<T> type;

  public Field(Class<T> type) {
    this.type = type;
  }
}

class Pick<V> {
  private final V value;
  private final Class<V> type;

  public Pick(V value, Class<V> type) {
    this.value = value;
    this.type = type;
  }
}

和問題所涉及的類:

class PickField<T> extends Field<Pick<T>> {

  public PickField(Class<Pick<T>> type) {
    super(type);
  }
}

現在這似乎被編譯器接受了。 不幸的是,我不知道/理解如何創建一個新的PickField實例,例如String picks。

這當然 - 不起作用:
new PickField<String>(Pick.class)

這是不允許的(我想我理解為什么):
new PickField<String>(Pick<String>.class)

那怎么辦呢? 或者整個方法是否以某種方式“ ”?

我認為PickField應僅使用Pick實例進行參數化。

所以這樣做應該沒問題:

class PickField<T extends Pick<T>> extends Field<T> {

    public PickField(Class<T> c) {
        super(c);
    }
}

然后,您可以使用以下方法實例化它:

PickField<SomeSpecificPick> instance = new PickField<>(SomeSpecificPick.class);

其中SomeSpecificPick定義為:

public class SomeSpecificPick extends Pick<SomeSpecificPick> {

    public SomeSpecificPick(SomeSpecificPick value, Class<SomeSpecificPick> type) {
        super(value, type);
    }
}

更多信息(與主題相關):

這里有各種各樣的問題。

首先,正如您所指出的,您無法在編譯時獲取參數化類的類,因為只為泛型類型編譯了一個類,而不是每個給定類型參數編譯一個類(例如, Pick<String>.class成語不能編譯,並且實際上沒有意義)。

再次提到, 使用Pick.class參數化PickField<String>構造函數將不會再次編譯,因為簽名不匹配。

您可以使用運行時慣用法來推斷正確的Pick<T>參數,但這會產生另一個問題:由於鍵入擦除T的類型參數在運行時將是未知的。

因此,您可以通過顯式轉換來參數化構造函數調用,如下所示:

new PickField<String>(
    (Class<Pick<String>>)new Pick<String>("", String.class).getClass()
);

...將使用“未經檢查的強制轉換”警告進行編譯( Type safety: Unchecked cast from Class<capture#1-of ? extends Pick> to Class<Pick<String>>取消選中強制轉換Type safety: Unchecked cast from Class<capture#1-of ? extends Pick> to Class<Pick<String>> )。

真正的問題可能是你為什么需要知道Pick類中的type值。

有一種方法,但並不是一個好方法。 你需要創建一個這樣的方法:

public static <I, O extends I> O toGeneric(I input) {
    return (O) input;
}

然后,您創建對象:

new PickField<String>(toGeneric(Pick.class));

就像我說的,不是一個好方法,因為你基本上只是騙算編譯器,但它的工作原理。

為了將泛型信息作為參數傳遞, Class<T>是不夠的。 你需要一些額外的力量來實現這一目標。 請參閱這篇文章 ,其中解釋了超類型令牌的含義。

簡而言之,如果您有以下課程:

public abstract class TypeToken<T> {

    protected final Type type;

    protected TypeToken() {
        Type superClass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() {
        return this.type;
    }
}

您可以使用它來存儲泛型類型信息,例如Pick<String>.class (這是非法的)。 訣竅是使用超類的泛型類型信息,可以通過Class.getGenericSuperclass()ParameterizedType.getActualTypeArguments()方法訪問它們。

我稍微修改了PickFieldPickField類,以便它們使用超類型令牌而不是Class<t> 請參閱修改后的代碼:

class Field<T> {

    private final TypeToken<T> type;

    public Field(TypeToken<T> type) {
        this.type = type;
    }
}

class Pick<V> {

    private final V value;

    private final TypeToken<V> type;

    public Pick(V value, TypeToken<V> type) {
        this.value = value;
        this.type = type;
    }
}

class PickField<T> extends Field<Pick<T>> {

    public PickField(TypeToken<Pick<T>> type) {
        super(type);
    }
}

以下是一個示例用法:

TypeToken<Pick<String>> type = new TypeToken<Pick<String>>() {};
PickField<String> pickField = new PickField<>(type);

由於TypeToken類是抽象的,因此需要對其進行子類化(這解釋了聲明結尾處的{}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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