How can I resolve the ambiguous methods caused by intersection types in Java generics?

advertisements

I just recently discovered that you can specify multiple types in a single type parameter bound (see example). Like any new tool, I've been trying to explore the possibilities of how this can be used (and misused). I crafted this example to help illustrate.

On the sample below, the compiler is giving me an error

dispatch(new AlphabetSoup());

The method dispatch(Demo.Soup) is ambiguous for the type Demo

I can understand this because either method signature matches. My question is how could this be resolved without changing the methods? If I wanted force a call to the Soup version I could downcast to Soup:

dispatch((Soup) new AlphabetSoup())

But I'm unsure how you'd force a call to the other version. Is it possible?

public class Demo {

    interface HasA { public char getA(); }
    interface HasB { public char getB(); }
    interface HasC { public char getC(); }

    interface Soup {
        public void eat();
    }

    class Alphabet implements HasA, HasB, HasC {
        public char getA() { return 'a'; }
        public char getB() { return 'b'; }
        public char getC() { return 'c'; }
    }

    class AlphabetSoup implements Soup,  HasA, HasB, HasC  {
        public void eat() { System.out.println("Mmm Mmm Good!"); }
        public char getA() { return 'a'; }
        public char getB() { return 'b'; }
        public char getC() { return 'c'; }
    }

    public void dispatch(Soup soup) {
        System.out.println("Eating some soup...");
        soup.eat();
    }

    public <T extends HasA & HasB & HasC> void dispatch(T letters) {
        System.out.println("Reciting ABCs...");
        System.out.println(letters.getA());
        System.out.println(letters.getB());
        System.out.println(letters.getC());
    }

    public void test() {
        dispatch(new Alphabet());
        dispatch(new AlphabetSoup());
    }

    public static void main(String[] args) {
        new Demo().test();
    }
}

-- Edit: Just learned that "multiple bounded type parameters are formally referred to as "Intersection Types"


Note that the error is not related to generics, you get the same result if you use interfaces and a type is the intersection:

public class AA {

    interface XX{};
    interface YY{};

    public void doSomething(XX x){}
    public void doSomething(YY x){}

    class XY implements XX,YY{

    }

    public void runner(){
        doSomething(new XY());
    }
}

You get the same error in "doSomething", the compiler cannot resolve the ambiguity. Do you want to interpret as XX or as YY? You have to specify it with a cast. But if you have a hierarchy, like "YY extends XX" and "XY implements YY", the compiler can infer the correct method to call.