泛型 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 模型值参数,例如 FloatParameterEnumParameter<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.classEnumParameter.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<?> 的子类型; TT<?> 超类型 。因此,根据通用子类型化规则, 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.