Christopher Schultz
Christopher Schultz

Reputation: 20862

Why won't Java resolve this <generic> method signature?

I broke a build today with code I think should work:

import java.util.Collections;
import java.util.HashMap;
import java.util.List;

public class EmptyListTest {
    public static void main(String[] args) {
        HashMap<Integer,List<String>> map = new HashMap<Integer,List<String>>();

        map.put(Integer.valueOf(0), Collections.emptyList());
    }
}

The Eclipse Java compiler has no problem with this code, but Oracle's javac fails with this message:

src/java/EmptyListTest.java:9: error: no suitable method found for put(Integer,List<Object>)
map.put(Integer.valueOf(0), Collections.emptyList());
            ^
     method Map.put(Integer,List<String>) is not applicable
       (argument mismatch; List<Object> cannot be converted to List<String>)
     method AbstractMap.put(Integer,List<String>) is not applicable
       (argument mismatch; List<Object> cannot be converted to List<String>)
     method HashMap.put(Integer,List<String>) is not applicable
       (argument mismatch; List<Object> cannot be converted to List<String>)
1 error
4 warnings

The obvious thing to do is to help-out the Oracle compiler by adding a cast to the Collections.emptyList(), but of course that causes its own compiler error (for both Eclipse and javac):

 /Users/chris/Documents/Eclipse/chadis-web/src/java/EmptyListTest.java:9: error: incompatible types: List<Object> cannot be converted to List<String>
         map.put(Integer.valueOf(0), (List<String>)Collections.emptyList());

I'm well aware that Java's support for generics is officially terrible, but this seems like a common case that ought to work. I know I can just instantiate an empty list manually with the correct data type, but that's wasteful. I know I can split-up this line of code into two (one with a declaration of List<String> and assignment to Collections.emptyList() and another with the call to Map<Integer,List<String>>.put(), but that's just more code for no good reason.

Is there something else I can do to convince Oracle's javac that this code is valid?

Update: Using Eclipse Luna (not sure compiler version) and Oracle Java 1.8.0_151.

Update 2: looks like this was caused by the presence of -source 1.5 as one of the arguments to the compiler. Using the default source for javac does not cause any errors. Strange that Collections.emptyList() behaves differently across versions when it hasn't changed since Java 1.5. There must have been some kind of change to the erasure-handling across versions that makes this work differently at different source-compatibility levels.

Upvotes: 0

Views: 145

Answers (1)

khelwood
khelwood

Reputation: 59112

In some versions of Java (pre-Java 8, including Java 8 with a source-compatibility setting less than 1.8) you need to specify the generic type so that emptyList will return a generic list rather than a raw one. You can do that by using:

Collections.<String>emptyList()

In more recent Java versions, the compiler can deduce the required generic type from the context, so you don't need to specify it. This deduction was added in Java 8 so that you don't have to specify the generic type at every step in the chain when using a stream.

Upvotes: 3

Related Questions