Suppose I have a generic class that I use which declared like this:
public class ConfigurableRuleKey<R extends Configurable & Rule> extends Key<R> {
private final R rule
public ConfigurableRuleKey(R rule) {
this.rule = rule;
}
/* Additional methods are declared here */
}
And I want to implement a factory method that checks if passed rule implements interface Configurable
, when create configurable rule or just create a basic key:
public static <R extends Rule> Key<R> create(R rule) {
if (rule instanceof Configurable) {
return new ConfigurableRuleKey<>(rule); //This will not compile
} else {
return new RuleKey<>(rule);
}
}
The problem is that in my factory method I can't pass rule to constructor of ConfigurableRuleKey
because it doesn't fit the declared generic constraints (event if I'd explicitly checked that it implements Configurable
). The question is how can I cast my rule instance so it will fit the constructor restrictions in ConfigurableRuleKey
?
Java does not support "multiple inheritance" (a class can only inherit from one superclass). However, it can be achieved with interfaces, because the class can implement multiple interfaces.
A class or struct can implement multiple interfaces. A class can inherit a base class and also implement one or more interfaces.
The Java compiler won't let you cast a generic type across its type parameters because the target type, in general, is neither a subtype nor a supertype.
We can subtype a generic class or interface by extending or implementing it. The relationship between the type parameters of one class or interface and the type parameters of another are determined by the extends and implements clauses.
I did find a way to do what you ask without using raw types, but it still involves a pair of unchecked casts:
@SuppressWarnings("unchecked")
public static <R extends Rule, T extends Rule & Configurable> Key<R> create(R rule) {
if (rule instanceof Configurable) {
return (Key<R>)new ConfigurableRuleKey<>((T)rule);
} else {
return new RuleKey<>(rule);
}
}
I'm not sure if this is better than sp00m's answer, but it is different, at least. :)
The intrinsic reason why this cannot be properly constructed within Java's generics appears to be that intersection types need to be constructed from concrete interfaces, and cannot be constructed from other type variables. Therefore, there is no way to construct T
in this example in such a way that it is bounded by R
. Without that capability, there is not even a way to have a magic method akin to Class.asSubclass
to abstract this behavior.
EDIT: Relatedly, it appears that Java 8 introduced anonymous intersection types where you can cast to multiple interfaces -- eg. (Configurable & Rule)rule
-- but even that doesn't help here since, for the same reason as above, you cannot cast to (Configurable & R)
. It would help to get rid of the type variable T
, however:
@SuppressWarnings("unchecked")
public static <R extends Rule> Key<R> create(R rule) {
if (rule instanceof Configurable) {
return (Key<R>)new ConfigurableRuleKey<>((Configurable & Rule)rule);
} else {
return new RuleKey<>(rule);
}
}
This should suit your needs:
@SuppressWarnings({ "rawtypes", "unchecked" })
public static <R extends Rule> Key<R> create(R rule) {
if (rule instanceof Configurable) {
return new ConfigurableRuleKey((Configurable) rule);
} else {
return new RuleKey<>(rule);
}
}
You manually checked that rule
is Configurable
, so the cast is safe.
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