7

I'm porting Java7 code to Java8, and I came up with a following problem. In my codebase I have two methods:

public static <T> ImmutableSet<T> append(Set<T> set, T elem) {
    return ImmutableSet.<T>builder().addAll(set).add(elem).build();
}

public static <T> ImmutableSet<T> append(Set<T> set, Set<T> elemSet) {
    ImmutableSet.Builder<T> newSet = ImmutableSet.builder();
    return newSet.addAll(set).addAll(elemSet).build();

Compiler returns error about ambiguous match for method append in following test:

@Test(expected = NullPointerException.class)
public void shouldAppendThrowNullPointerForNullSecondSet() {
    ImmutableSet<Integer> obj = null;
    CollectionUtils.append(ImmutableSet.of(1), obj);
}

Compiler error:

reference to append is ambiguous both method append(java.util.Set,T) in CollectionUtils and method append(java.util.Set,java.util.Set) in CollectionUtils match

How to rewrite these functions to work with type inference from introduced with Java8?

Xter
  • 267
  • 1
  • 8
  • 1
    You can make it work by having `ImmutableSet obj = null; ImmutableSet set = ImmutableSet.of(1); append(set, obj);`. – Tunaki Mar 15 '16 at 10:20
  • Cannot reproduce in ideone (using HashSet instead of ImmutableSet, but that shouldn't matter from a type inference perspective) http://ideone.com/oT6SbF. – Andy Turner Mar 15 '16 at 10:24
  • @AndyTurner It does fail to compile with jdk 1.8.0_51 (and Eclipse Mars.2). – Tunaki Mar 15 '16 at 10:24
  • @Tunaki So is it an issue with Eclipse Mars.2, rather than JDK 1.8.0_51, since ideone claims to be using that version of Java? – Andy Turner Mar 15 '16 at 10:26
  • 2
    @AndyTurner I meant that both of them fail to compile this. I tested this with the real `ImmutableSet` also, maybe it matters (which is the difference with your ideone test). – Tunaki Mar 15 '16 at 10:26

1 Answers1

7

You've found the new Generalized Target-type Inference improvements in Java 8. There's a few stack overflow questions on it. Such as this one.

Java 8 can infer the return type of a generic based on the method it's passed to as an argument. So when you call CollectionUtils.append(ImmutableSet.of(1), obj), Java 8 is attempting to return an immutable set from the static of call that matches one of your append methods. In this instance, it could think about returning a ImmutableSet<Object> instead of the ImmutableSet<Integer> that you're clearly trying to return. And then it's unclear if you're calling append(Set<Object>, Object), or append(Set<Integer>, Set<Integer>).

The easiest solution is to rename the second method appendAll. Or, you could follow the suggested fix here and change your call to something like :

CollectionUtils.append(ImmutableSet.<ImmutableSet<Integer>>of(1), obj);

I'd stick with renaming the second method myself though. It will save other developers the same grief when they try to use the library.

Community
  • 1
  • 1
AndyN
  • 2,075
  • 16
  • 25