A general Java type class cannot be assigned to a variable whose class type is limited to the top supersymmetry type

I use Java 8. There are some simple classes in my design that model parameter parameters, such as FloatParameter or EnumParameter<E> . A have a common superclass of these classes ( GenericParameter<T> ), which implements the parameter name and its default value. FloatParameter implement other attributes specific to them, such as a range in the case of a FloatParameter .

In addition, I want to work with parameter types regardless of their specific type. But I still want to bind types so that they are subtypes of GenericParameter<T> . To do this, I created a method such as process(Class<? extends GenericParameter<?>> paramType) .

Now the problem is that EnumParameter.class cannot be assigned to a variable of type Class<? extends GenericParameter<?>> Class<? extends GenericParameter<?>> , and FloatParameter.class maybe .

Next, I list the code for the classes to make it more understandable and reproducible:

 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 

Finally, there are no problems using the non-core base 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 

Please have any suggestions?

I can go with process(Class<?> paramType) as a workaround or make throws, but I would like to use the static type checking by the compiler.

EDIT:

I would like to use listing when registering factories that create GUI components for each type of parameter. The code looks like this:

 addParameterComponentFactory(EnumParameter.class, new ParameterComponentFactory() { ... }) 

In this case, the compiler will check the added parameter type at compile time. Also, the code will be more self-evident.

EDIT 2:

I am currently using the proposed approach to introduce a type parameter for the addParameterComponentFactory method. The signature is as follows:

 public static <P extends GenericParameter<?>> addParameterComponentFactory(Class<P> clazz, ParameterComponentFactory pcf) 

With this definition, I can specify TypedParameter.class ( EnumParameter.class is also one type parameter), and also get a static type check.

+5
source share
1 answer

Let's start with the core bits of your API. You have a generic Parameter<T> type that represents some named parameter with a value of type T You have specialized GUI components designed to edit or display certain types of parameters, and you want to be able to register factories to create these components.

 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> {} 

If I understand you correctly, you have encountered difficulties in figuring out how to declare a method that statically establishes a connection between some Parameter and factory for GUI components specialized for this type. For example, you want to write this:

 addComponentFactory(EnumParameter.class, EnumParameterComponent::new); // OK addComponentFactory(FloatParameter.class, FloatParameterComponent::new); // OK addComponentFactory(FloatParameter.class, EnumParameterComponent::new); // ERROR! 

The problem is with the general subtyping rules, and you can get around them using a type variable instead of an inline template. This should give you the type check you want, without the need for an unpleasant cast:

 static <P extends Parameter> void addComponentFactory( final Class<P> parameterType, final ParameterComponentFactory<? extends P> factory) { ... } 

Explanation [1]

Explain the difference between introducing the new type P extends Parameter<?> Used in Class<P> and specifying Class<? extends Parameter<?>> Directly Class<? extends Parameter<?>>

It's complicated, so carry me. Let's talk a bit about wildcards, raw types, and conversions. Consider the following:

 // 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; 

Scenarios 1 (a) and 1 (b) both compile for the same reason: since the raw type G can undergo an unchecked conversion to any parameterized form type G<T_1, ..., T_n> .

Scenario 2 does NOT . But why?

In Scenario 2, neither side of the second assignment is a raw type. For an assignment to be valid , there must be either an identity conversion or an extension of the conversion from the right type to the left type. To extend conversions to reference types, the left type must be a supertype of the right type. When these types are common, the general subtyping rules give expression. In particular, type arguments on the left should contain type arguments on the right.

A valid assignment from Class<String> to Class<? extends Object> Class<? extends Object> . Class<String> is a common subtype of Class<? extends Object> Class<? extends Object> because ? extends Object ? extends Object contains a String . In Scenario 2, the second assignment must be valid, the GenericParameter<?> Must contain the GenericParameter , but it is not. T not a subtype of T<?> ; T is a supertype of T<?> . Thus, according to the general rules of subtyping, Class<T> not a subtype of Class<T<?>> , and the assignment is not valid.

So why does the following work?

 public static <P extends GenericParameter<?>> addParameterComponentFactory( Class<P> clazz, ParameterComponentFactory pcf) addParameterComponentFactory(EnumParameter.class, new ParameterComponentFactory() {}) 

In the above call, the output type on P completely controlled by Class<P> . You pass a Class<EnumParameter> , so P in this case is bound to the raw type EnumParameter . To limit P extends GenericParameter<?> , GenericParameter<?> Must be assigned from EnumParameter , and it can be assigned through an uncontrolled conversion, as in scenarios 1 (a) and 1 (b).

[1] This blatant plug-in explanation combines the other excellent stack overflow responses, mostly radiodef .

+1
source

Source: https://habr.com/ru/post/1271928/


All Articles