I use Java 8. In my design a have a few simple classes which model value parameters such as FloatParameter
or EnumParameter<E>
. A have a common generic super class of these classes (GenericParameter<T>
) which implements parameter name and its default value. The sub classes implement other attributes specific to them such as range in case of FloatParameter
.
Moreover, I want to work with the types of the parameters regardless of their specific type. But I still want to bound the types in the way that they are sub types of GenericParameter<T>
. In order to do that, I created a method such as process(Class<? extends GenericParameter<?>> paramType)
.
Now, the problem is that the EnumParameter.class
cannot be assigned to a variable of type Class<? extends GenericParameter<?>>
while FloatParameter.class
can be.
Further I list the code for the classes to make it more clear 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, when using a non-generic base class, there is no problem:
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, do you have any suggestions?
I can go with process(Class<?> paramType)
as a workaround or do casts, but I wanted to benefit from the static type checking by the compiler.
EDIT:
I would like to use the cast when registering factories that produce GUI components for each parameter type. The code looks like:
addParameterComponentFactory(EnumParameter.class, new ParameterComponentFactory() { ... })
In such case, the compiler would check the added parameter type at compile time. Also the code would be more self-explaining.
EDIT 2:
Currently, I am using the suggested approach to introduce a type parameter for the addParameterComponentFactory
method. The signature looks like this:
public static <P extends GenericParameter<?>> addParameterComponentFactory(Class<P> clazz, ParameterComponentFactory pcf)
With this definition I am able to specify TypedParameter.class
(EnumParameter.class
- also one type param) as well as I obtain the static type checking.
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 specific 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're running into trouble figuring out how to
declare a method that statically enforces a relationship between some
Parameter
type and a factory for GUI components specialized for that type.
For example, you want to be able to write this:
addComponentFactory(EnumParameter.class, EnumParameterComponent::new); // OK
addComponentFactory(FloatParameter.class, FloatParameterComponent::new); // OK
addComponentFactory(FloatParameter.class, EnumParameterComponent::new); // ERROR!
The problem is related to the rules of generic subtyping, and you can work around them by employing a type variable instead of an embedded wildcard. This should give you the type checking you want, without the need for nasty casting:
static <P extends Parameter> void addComponentFactory(
final Class<P> parameterType,
final ParameterComponentFactory<? extends P> factory) { ... }
Explain the difference between introducing a new type
P extends Parameter<?>
used inClass<P>
and stating directlyClass<? extends Parameter<?>>
This is complicated, so bear with 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: because a raw type
G
may undergo an unchecked conversion
to any parameterized type of the form G<T_1, ..., T_n>
.
Scenario 2 does NOT compile. But why?
In Scenario 2, neither side in the second assignment is a raw type. For the assignment to be valid, there must be either an identity conversion or a widening conversion from the right-hand type to the left-hand type. For widening conversions on reference types, the left-hand type must be a supertype of the right-hand type. When these types are generic, the rules of generic subtyping apply. Specifically, the type arguments on the left-hand side must contain the type arguments on the right-hand side.
An assignment from Class<String>
to Class<? extends Object>
is valid.
Class<String>
is a generic subtype of Class<? extends Object>
because
? extends Object
contains String
. In Scenario 2, for the second
assignment to be valid, GenericParameter<?>
would have to contain
GenericParameter
, but it doesn't. T
is not a subtype of T<?>
;
T
is a supertype of T<?>
. Thus, by the generic subtyping rules,
Class<T>
is 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 call above, type inference on P
is driven entirely by the
Class<P>
argument. You are passing a Class<EnumParameter>
, so P
in this case gets bound to the raw type EnumParameter
. For the constraint
P extends GenericParameter<?>
to be satisfied, GenericParameter<?>
must be assignable from EnumParameter
, and it is assignable via an
unchecked conversion, just like in Scenarios 1(a) and 1(b).
[1] This explanation is blatant plagarism
an amalgamation of other excellent Stack Overflow answers, mostly by
radiodef.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With