Skip to content

Instantly share code, notes, and snippets.

@aeisenberg
Created November 16, 2016 02:50
Show Gist options
  • Save aeisenberg/de558a35d09de0184be2707cfe42c921 to your computer and use it in GitHub Desktop.
Save aeisenberg/de558a35d09de0184be2707cfe42c921 to your computer and use it in GitHub Desktop.
import java.util.Collection;
public class Ambiguity {
public void foo(CriteriaBuilder builder) {
// change Object -> Number (or any other type) and it compiles
Expression<Object> objectExpression = null;
Expression<Collection<Object>> collectionOfObjectExpression = null;
builder.isNotMember(objectExpression, collectionOfObjectExpression);
}
}
class Expression<T> { }
class CriteriaBuilder {
public <E,C extends Collection<E>> void isNotMember(E elem, Expression<C> collection) {}
public <E,C extends Collection<E>> void isNotMember(Expression<E> elem, Expression<C> collection) {}
}
@aeisenberg
Copy link
Author

When I compile with java 1.8, I get this:

$ javac Ambiguity.java
Ambiguity.java:10: error: reference to isNotMember is ambiguous
		builder.isNotMember(objectExpression, collectionOfObjectExpression);
		       ^
  both method <E#1,C#1>isNotMember(E#1,Expression<C#1>) in CriteriaBuilder and method <E#2,C#2>isNotMember(Expression<E#2>,Expression<C#2>) in CriteriaBuilder match
  where E#1,C#1,E#2,C#2 are type-variables:
    E#1 extends Object declared in method <E#1,C#1>isNotMember(E#1,Expression<C#1>)
    C#1 extends Collection<E#1> declared in method <E#1,C#1>isNotMember(E#1,Expression<C#1>)
    E#2 extends Object declared in method <E#2,C#2>isNotMember(Expression<E#2>,Expression<C#2>)
    C#2 extends Collection<E#2> declared in method <E#2,C#2>isNotMember(Expression<E#2>,Expression<C#2>)
1 error

This makes sense. Expression<Object> is a subclass of Object, so it can match the first parameter of either method. And Expression<Collection<Object>> can match the second parameter of either method. So, there is ambiguity here.

If you change Object to Number or any other type and recompile, javac does not complain because Expression<Collection<Number>> is the type of the second parameter, but the first method is expecting Expression<Collection<Expression<Number>>>. So, only the second method can match.

Perhaps the bug is in Java 1.6?

@odrotbohm
Copy link

It still doesn't entirely make sense to me:

  1. To consider the first method a match, E has to be bound to Expression<Object>. That'd mean the second parameter is expected to be an Expression<Collection<Expression<Object>>>. But we hand in a Expression<Collection<Object>> so that shouldn't match, should it?
  2. There is no Java 6 involved at all. It also compiles if you use a JDK 8 compiler but set the source and target level to 1.6.
  3. My actual question is: if I have the two variables of type Expression<Object> and Expression<Collection<Object>>, how do I tell the compiler I want to bind to the second method? There doesn't seem to be any way (through type hints, casts or the like) to get this to work.

@odrotbohm
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment