Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

java

generics

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"

like image 577
Mark Renouf Avatar asked May 02 '09 22:05

Mark Renouf


People also ask

How do you overcome ambiguity in Java?

The inclusion of generics gives rise to a new type of error that you must guard against ambiguity. Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict. Here is an example that involves method overloading.

What is ambiguous method call in Java?

This ambiguous method call error always comes with method overloading where compiler fails to find out which of the overloaded method should be used.

What is ambiguity error in method overloading?

There are ambiguities while using variable arguments in Java. This happens because two methods can definitely be valid enough to be called by data values. Due to this, the compiler doesn't have the knowledge as to which method to call.

What is ambiguity problem?

Ambiguity is the possibility of having more than one answer. If something is ambiguous, it means it might have more than one answer or more than one solution to the problem.


2 Answers

Let me explain this with a very simple program:

Code below illustrated couse of The method is ambiguous for the type compiler error.

public class AmbiguousMethodOwner {
            void ambiguousMethod(Comparable c){}
            void ambiguousMethod(Serializable c){}
            void test() {
                   ambiguousMethod("bar");
           }
     }

The problem now is obvious: since String implements both Comparable and Serializable, the compiler cannot know which method you intend to call.

A simple cast will solve the problem:

ambiguousMethod((Comparable)"bar");

http://www.javaneverdie.com/java/the-method-is-ambiguous-for-the-type/

In our case method dispatch is creating problem. See

class AlphabetSoup implements Soup,  HasA, HasB, HasC 

and

public void dispatch(Soup soup)
 public <T extends HasA & HasB & HasC> void dispatch(T letters) {

now if you call dispatch(new AlphabetSoup()); compiler would be confused as to which version of dispatch should be called ?

like image 117
AZ_ Avatar answered Nov 07 '22 18:11

AZ_


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.

like image 32
Uri Avatar answered Nov 07 '22 18:11

Uri