10

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?

2 Answers 2

3

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);
    }
}
Sign up to request clarification or add additional context in comments.

3 Comments

My IDE tells me that this code won't even compile but in fact it seems that it does. :) Thanks. I think I'll report this issue to Support team of my IDE.
@SimY4: Just out of curiosity, just what is it that IDE complains about?
I'm using IntelliJ Idea 14 Early Access Preview
2

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.

8 Comments

You’re using raw types and suppress all the warnings, I think that is not what the OP wants.
@WilQu: I doubt it is possible to do what OP wants without breaking generics a bit, because in order to actually instantiate a ConfigurableRuleKey directly, a concrete type which is both Configurable and Rule is necessary, and the create function does not have access to any such concrete type.
@WilQu I'm not suppressing all the warnings, only the rawtypes so that the compiler does not check the generics (I'm not sure it can be done otherwise), and the unchecked since the compiler can't know if the cast is safe (while it for sure is, manually checked thanks to instanceof).
@sp00m if you don't use generics, then the cast is not even necessary.
You can also try (Configurable & Rule) in the cast. This is legal construct at least in Java 8.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.