Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java generic object with multiple interfaces casting

Tags:

java

generics

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?

like image 371
SimY4 Avatar asked Sep 03 '14 08:09

SimY4


People also ask

Can an object implement multiple interfaces in Java?

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.

Can an object have multiple interfaces?

A class or struct can implement multiple interfaces. A class can inherit a base class and also implement one or more interfaces.

Can you cast generic type Java?

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.

Can a generic class extend an interface?

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.


2 Answers

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);
    }
}
like image 136
Dolda2000 Avatar answered Sep 19 '22 11:09

Dolda2000


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.

like image 24
sp00m Avatar answered Sep 22 '22 11:09

sp00m