无法将通用Java类的类分配给Class类型由泛型超类型限制的变量

时间:2017-09-19 10:11:47

标签: java generics wildcard upperbound

我使用Java 8.在我的设计中,有一些简单的类可以为FloatParameterEnumParameter<E>等值参数建模。 A具有这些类(GenericParameter<T>)的通用超类,它实现了参数名称及其默认值。子类实现了特定于它们的其他属性,例如FloatParameter的范围。

此外,我想处理参数的类型,无论其具体类型如何。但我仍然希望以类型GenericParameter<T>的子类型的方式绑定类型。为此,我创建了一个方法,例如process(Class<? extends GenericParameter<?>> paramType)

现在,问题是EnumParameter.class 无法分配给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.classEnumParameter.class - 也是一种类型参数)以及获取静态类型检查。

1 个答案:

答案 0 :(得分:1)

让我们从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) { ... }

解释 [1]

  

解释引入P extends Parameter<?>中使用的新类型Class<P>与陈述之间的区别   直接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>Class<String>形式的任何参数化类型。

场景2执行 NOT 编译。但为什么呢?

在场景2中,第二个分配中的任何一方都不是原始类型。为了 unchecked conversion, 必须有身份转换或assignment to be valid 从右手型到左手型。用于扩大转换次数 引用类型,左手类型必须是右手类型的超类型。 当这些类型是通用的时,widening conversion的规则 应用。具体来说,左侧的类型参数必须 generic subtyping 右侧的类型参数。

Class<? extends Object>Class<String>的作业有效。 Class<? extends Object>? extends Object的通用子类型,因为 String 包含 GenericParameter<?>。在场景2中,第二个 赋值有效,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 必须可以从NULL分配,并且可以通过分配 未经检查的转换,就像在场景1(a)和1(b)中一样。

[1] 这个解释是明目张胆的加勒比主义 其他出色的Stack Overflow答案的合并,主要是由 contain