tags:

views:

619

answers:

4

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"

+1  A: 

Note that you do not have a real problem, as the methods you are interested in calling are already called due to dynamic binding.

Running dispatch((Soup) new AlphabetSoup()); yields:

Reciting ABCs...
a
b
c
Eating some soup...
Mmm Mmm Good!

Hence, the AlphabetSoup method are already called due to basic polymorphic behavior.

Yuval A
+3  A: 

The compiler is right, and saves you from a mess.

AlphaBetSoup is a subtype of soup and also a subtype of HasA, HasB, and HasC

Therefore, it fits the bill for both versions of Dispatch

Since Soup is not a subtype of HasA, HasB, or HasC, it also can't say that one version is more "specific" than the other.

Therefore you'll get the error correctly.

Overloaded method should not be ambiguous. If you have a type that mixes both types and you had an overload for each, change your hierarchy or get rid of an overload. It's wrong use of subtyping and overloading.

Uri
+1  A: 

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.

GClaramunt
+1  A: 

Not that you should keep the overloaded dispatch method (I upvoted Uri for that reason), but you can force the generic version to be called by trying:

demo.<AlphabetSoup>dispatch(new AlphabetSoup());

or call the soup version with:

demo.dispatch((Soup) new AlphabetSoup());

The better way around this, though, is to not have the overloaded dispatch method in the first place.

void dispatchSoup(Soup soup);
<T extends HasA & HasB & HasC> void dispatchLetters(T letters);
Steve Reed