Mix Java 8 compiler with overloaded methods

While updating the application to Java 8, I encountered a strange problem with google guava newArrayList in several places.

Take a look at this example:

 import com.google.common.collect.UnmodifiableIterator; import javax.naming.NamingException; import javax.naming.directory.Attribute; import javax.naming.directory.BasicAttribute; import java.util.ArrayList; import static com.google.common.collect.Iterators.forEnumeration; import static com.google.common.collect.Lists.newArrayList; public class NewArrayListIssue { public static void main(String[] args) throws NamingException { UnmodifiableIterator<?> elements = forEnumeration(getEnumeration().getAll()); System.out.println("declarefirst = " + newArrayList(elements)); // calls newArrayList(Iterator<? extends E> elements) ArrayList directCopy = newArrayList(forEnumeration(getEnumeration().getAll())); System.out.println("useDirectly = " + directCopy); //calls newArrayList(E... elements) } public static Attribute getEnumeration(){ return new BasicAttribute("foo",1); } } 

In the first example, when I first take the UnmodifiableIterator into my own variable and then call newArrayList , I get what I expect, these are Iterators values ​​copied to the new List .

In the second example, when forEnumeration goes directly to the newArrayList method, I return a List with the containing iterator (which contains the value).

According to Intellij, he believes that both method calls should be newArrayList(Iterator<? extends E> elements) , but I found that when debugging, the second call really goes into newArrayList(E... elements) .

This only happens when compiling with a Java8-oriented Oracle JDK8. If I'm aiming for 7, it works great.

+7
java guava java-8
source share
2 answers

The problem is that the compiler believes that newArrayList(Iterator<? extends E>) not applicable (possibly due to this error ), and then quietly selects the generic varargs method, which is always applicable (which indicates the danger of such an overload) when you are not using a specific element type for your list of results.

The error appears with wildcard types, i.e. in your code Attribute.getAll() returns NamingEnumeration<?> , so the result of forEnumeration is UnmodifiableIterator<?> , which the compiler refuses to assign Iterable<? extends E> Iterable<? extends E> , parameter type newArrayList . If you return the return value of the internal call to Enumeration , the problem disappears, just like when you drop the return value of the external call to Iterator .

I do not see a simple short-term solution to this problem. In the end, I do not understand why you did not use List<?> directCopy=Collections.list(getEnumeration().getAll()); first of all...

Please note that if you want to find all occurrences of this problem, you can simply use the corrected version of guava in which newArrayList(E...) was deleted and check all compiler errors (assuming that you do not have many cases where you really want to call this overload). After rewriting call sites, you can return to the original guava.

+7
source share

I saw how this happens with overloaded methods and generic types. In this case, the more general version of newArrayList() is selected when the parameter is not explicitly typed.

I have no technical explanation for you, but I would recommend that you force the desired method overload by casting:

 ArrayList directCopy = newArrayList((Iterator)forEnumeration(getEnumeration().getAll())); 
+1
source share

All Articles