泛型 Java 类型的 Class 无法分配给其 Class 类型上限为泛型超类型的变量
Class of a generic Java type cannot be assigned to a variable whose Class type is upper bounded by a generic super type
我使用 Java 8. 在我的设计中有一些简单的 classes 模型值参数,例如 FloatParameter
或 EnumParameter<E>
。 A 具有这些 classes (GenericParameter<T>
) 的通用超类 class,它实现了参数名称及其默认值。 sub classes 实现其他特定于它们的属性,例如 FloatParameter
.
的范围
此外,我想使用参数的类型,而不考虑它们的具体类型。但我仍然想以它们是 GenericParameter<T>
的子类型的方式来绑定类型。为此,我创建了一个方法,例如 process(Class<? extends GenericParameter<?>> paramType)
.
现在,问题是 EnumParameter.class
不能 赋值给 Class<? extends GenericParameter<?>>
类型的变量,而 FloatParameter.class
可以是。
此外,我列出了 classes 的代码,以使其更加清晰和可重现:
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
最后,使用非泛型基class,没有问题:
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>
type 表示一些具有 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) { ... }
说明[1]
Explain the difference between introducing a new type P extends Parameter<?>
used in Class<P>
and stating
directly Class<? extends Parameter<?>>
这很复杂,请耐心等待。让我们谈谈通配符,raw
类型和转换。考虑以下因素:
// 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
可能会经历 unchecked conversion
G<T_1, ..., T_n>
.
形式的任何参数化类型
场景 2 不 编译。但是为什么?
在场景 2 中,第二个赋值的两边都不是原始类型。为了
assignment to be valid,
必须有身份转换或 widening conversion
从右手型到左手型。为了扩大转化率
引用类型,左侧类型必须是右侧类型的超类型。
当这些类型是泛型时, generic subtyping 的规则
申请。具体来说,左侧的类型参数必须
contain
右侧的类型参数。
从 Class<String>
到 Class<? extends Object>
的分配有效。
Class<String>
是 Class<? extends Object>
的通用子类型,因为
? extends Object
包含String
。在场景 2 中,对于第二个
分配有效,GenericParameter<?>
必须包含
GenericParameter
,但事实并非如此。 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]此解释公然抄袭
其他优秀的 Stack Overflow 答案的合并,主要是
radiodef.
我使用 Java 8. 在我的设计中有一些简单的 classes 模型值参数,例如 FloatParameter
或 EnumParameter<E>
。 A 具有这些 classes (GenericParameter<T>
) 的通用超类 class,它实现了参数名称及其默认值。 sub classes 实现其他特定于它们的属性,例如 FloatParameter
.
此外,我想使用参数的类型,而不考虑它们的具体类型。但我仍然想以它们是 GenericParameter<T>
的子类型的方式来绑定类型。为此,我创建了一个方法,例如 process(Class<? extends GenericParameter<?>> paramType)
.
现在,问题是 EnumParameter.class
不能 赋值给 Class<? extends GenericParameter<?>>
类型的变量,而 FloatParameter.class
可以是。
此外,我列出了 classes 的代码,以使其更加清晰和可重现:
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
最后,使用非泛型基class,没有问题:
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>
type 表示一些具有 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) { ... }
说明[1]
Explain the difference between introducing a new type
P extends Parameter<?>
used inClass<P>
and stating directlyClass<? extends Parameter<?>>
这很复杂,请耐心等待。让我们谈谈通配符,raw 类型和转换。考虑以下因素:
// 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
可能会经历 unchecked conversion
G<T_1, ..., T_n>
.
场景 2 不 编译。但是为什么?
在场景 2 中,第二个赋值的两边都不是原始类型。为了 assignment to be valid, 必须有身份转换或 widening conversion 从右手型到左手型。为了扩大转化率 引用类型,左侧类型必须是右侧类型的超类型。 当这些类型是泛型时, generic subtyping 的规则 申请。具体来说,左侧的类型参数必须 contain 右侧的类型参数。
从 Class<String>
到 Class<? extends Object>
的分配有效。
Class<String>
是 Class<? extends Object>
的通用子类型,因为
? extends Object
包含String
。在场景 2 中,对于第二个
分配有效,GenericParameter<?>
必须包含
GenericParameter
,但事实并非如此。 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]此解释公然抄袭
其他优秀的 Stack Overflow 答案的合并,主要是
radiodef.