[英]Class of a generic Java type cannot be assigned to a variable whose Class type is upper bounded by a generic super type
我使用FloatParameter
。在我的設計中,有一些簡單的類可以對值參數進行建模,例如FloatParameter
或EnumParameter<E>
。 A具有這些類的通用泛型超類( GenericParameter<T>
),該超類實現參數名稱及其默認值。 子類實現特定於它們的其他屬性,例如FloatParameter
范圍。
此外,無論參數的具體類型如何,我都希望使用它們的類型。 但是我仍然想以它們是GenericParameter<T>
子類型的方式來綁定類型。 為了做到這一點,我創建了一個諸如process(Class<? extends GenericParameter<?>> paramType)
。
現在,問題在於EnumParameter.class
無法分配給Class<? extends GenericParameter<?>>
類型的變量Class<? extends GenericParameter<?>>
Class<? extends GenericParameter<?>>
而FloatParameter.class
可以 。
此外,我列出了這些類的代碼,以使其更加清晰和可重復:
public class GenericParameter<T> {
protected String name;
protected T defaultValue;
}
public class FloatGenericParameter extends GenericParameter<Float> {
...
}
public class TypedGenericParameter<T> extends GenericParameter<T> {
...
}
Class<? extends GenericParameter<?>> fgpc = FloatGenericParameter.class; // ok
Class<? extends GenericParameter<?>> tgpc = TypedGenericParameter.class; // error: incompatible types: Class<TypedGenericParameter> cannot be converted to Class<? extends GenericParameter<?>>
Class<? extends GenericParameter> tgpc2 = TypedGenericParameter.class; // warning: [rawtypes] found raw type: GenericParameter
最后,當使用非通用基類時,沒有問題:
public class Parameter {
....
}
public class FloatParameter extends Parameter {
...
}
public class TypedParameter<T> extends Parameter {
...
}
Class<? extends Parameter> fpc = FloatParameter.class; // ok
Class<? extends Parameter> tpc = TypedParameter.class; // ok
請,您有什么建議嗎?
我可以將process(Class<?> paramType)
用作解決方法或進行強制轉換,但我想從編譯器的靜態類型檢查中受益。
編輯:
注冊要為每種參數類型生成GUI組件的工廠時,我想使用強制類型轉換。 代碼如下:
addParameterComponentFactory(EnumParameter.class, new ParameterComponentFactory() { ... })
在這種情況下,編譯器將在編譯時檢查添加的參數類型。 代碼也將更加不言自明。
編輯2:
當前,我正在使用建議的方法為addParameterComponentFactory
方法引入類型參數。 簽名如下所示:
public static <P extends GenericParameter<?>> addParameterComponentFactory(Class<P> clazz, ParameterComponentFactory pcf)
通過此定義,我可以指定TypedParameter.class
( EnumParameter.class
也是一種類型參數),並且可以獲得靜態類型檢查。
讓我們從您的API的核心部分開始。 您有一個泛型Parameter<T>
類型,該類型表示一些名為T
的值的已命名參數。 您具有專門用於編輯或顯示特定類型參數的GUI組件,並且希望能夠注冊工廠來創建這些組件。
class Parameter<T> {
String name;
T defaultValue;
}
class ParameterComponent<P extends Parameter> {
void setParameter(final P p) {}
}
interface ParameterComponentFactory<P extends Parameter> {
ParameterComponent<P> newComponent();
}
class FloatParameter extends Parameter<Float> {}
class FloatParameterComponent extends ParameterComponent<FloatParameter> {}
class EnumParameter extends Parameter<Enum> {}
class EnumParameterComponent extends ParameterComponent<EnumParameter> {}
如果我對您的理解正確,那么您將難以確定如何聲明一種方法來靜態地強制某種Parameter
類型與專用於該類型的GUI組件的工廠之間的關系。 例如,您希望能夠編寫以下代碼:
addComponentFactory(EnumParameter.class, EnumParameterComponent::new); // OK
addComponentFactory(FloatParameter.class, FloatParameterComponent::new); // OK
addComponentFactory(FloatParameter.class, EnumParameterComponent::new); // ERROR!
問題與通用子類型的規則有關,您可以通過使用類型變量而不是嵌入式通配符來解決這些問題。 這應該為您提供所需的類型檢查,而不需要討厭的轉換:
static <P extends Parameter> void addComponentFactory(
final Class<P> parameterType,
final ParameterComponentFactory<? extends P> factory) { ... }
解釋引入新的類型
P extends Parameter<?>
在Class<P>
使用的P extends Parameter<?>
Class<P>
和直接聲明Class<? extends Parameter<?>>
Class<? extends Parameter<?>>
這很復雜,請多多包涵。 讓我們談談通配符,原始類型和轉換。 考慮以下:
// Scenario 1(a)
GenericParameter raw = /* some value */;
GenericParameter<?> wc = raw;
// Scenario 1(b)
Class raw = GenericParameter.class;
Class<?> wc = raw;
// Scenario 2
Class<GenericParameter> classOfRaw = GenericParameter.class;
Class<GenericParameter<?>> classOfWC = classOfRaw;
場景1(a)和1(b)均出於相同的原因進行編譯:因為原始類型G
可能未經檢查就轉換為G<T_1, ..., T_n>
形式的任何參數化類型。
方案2 無法編譯。 但為什么?
在方案2中,第二個分配中的任何一方都不是原始類型。 為了使分配有效 ,必須存在從右手類型到左手類型的身份轉換或擴展轉換 。 為了擴大引用類型的轉換,左手類型必須是右手類型的超類型。 當這些類型是通用類型時,將應用通用子類型化規則。 具體而言,左側的類型實參必須在右側包含類型實參。
從Class<String>
到Class<? extends Object>
Class<? extends Object>
有效。 Class<String>
是Class<? extends Object>
的泛型子類型Class<? extends Object>
Class<? extends Object>
因為? extends Object
? extends Object
包含 String
。 在方案2中, GenericParameter<?>
第二個分配有效, GenericParameter<?>
必須包含GenericParameter
,但不是。 T
不是 T<?>
的子類型; T
是T<?>
的超 T<?>
。 因此,根據通用子類型化規則, Class<T>
不是Class<T<?>>
的子類型,並且該分配無效。
那為什么下面的工作呢?
public static <P extends GenericParameter<?>> addParameterComponentFactory(
Class<P> clazz,
ParameterComponentFactory pcf)
addParameterComponentFactory(EnumParameter.class, new ParameterComponentFactory() {})
在上面的調用中,對P
類型推斷完全由Class<P>
參數驅動。 您正在傳遞Class<EnumParameter>
,因此在這種情況下P
綁定到原始類型EnumParameter
。 為了滿足條件P extends GenericParameter<?>
要求, GenericParameter<?>
必須可以從EnumParameter
分配,並且可以通過未經檢查的轉換來分配,就像情況1(a)和1(b)一樣。
[1]這解釋是
明顯的抄襲
等優良堆棧溢出的答案的合並,主要由radiodef 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.