Java 8编译器混淆使用重载方法

时间:2015-01-22 00:44:30

标签: java java-8 guava

将应用程序升级到Java 8时,我在几个地方遇到了google guava' newArrayList的奇怪问题。

看一下这个例子:

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);
    }
}

在第一个示例中,当我首先将UnmodifiableIterator放入其自己的变量中,然后调用newArrayList时,我得到了我期望的内容,即Iterators值被复制到新的List中。

forEnumeration直接进入newArrayList方法的第二个示例中,我返回一个List,其中包含迭代器(包含值)。

根据Intellij,认为两个方法调用应该是newArrayList(Iterator<? extends E> elements),但我在调试时发现第二个调用实际上是newArrayList(E... elements)

只有当我使用针对Java8的Oracle JDK8进行编译时才会发生这种情况。如果我的目标是7,那就可以了。

2 个答案:

答案 0 :(得分:7)

问题是编译器认为newArrayList(Iterator<? extends E>)不适用(可能是因为this bug)然后默默地选择总是适用的通用varargs方法(当你没有为结果列表使用特定的元素类型时,显示这种重载的危险。

该错误出现在通配符类型中,即在您的代码中Attribute.getAll()返回NamingEnumeration<?>因此forEnumeration的结果为UnmodifiableIterator<?>,编译器拒绝将其分配给{ {1}},参数类型为Iterable<? extends E>。如果将内部调用的返回值强制转换为newArrayList,则问题将消失,就像将外部调用的返回值强制转换为Enumeration时一样。

我没有看到针对此问题的简单短期解决方案。毕竟,我不明白你为什么不首先使用Iterator ......

请注意,如果要查找此问题的所有实例,您可以只使用已删除List<?> directCopy=Collections.list(getEnumeration().getAll());的已修补版本的guava并检查所有编译器错误(假设您没有很多情况你真正想要调用这个重载的地方)。重写呼叫站点后,您可以返回原始番石榴。

答案 1 :(得分:1)

我发现重载方法和泛型类型会发生这种情况。在这种情况下,当未明确键入参数时,将选择更通用的newArrayList()版本。

我没有为您提供技术说明,但我建议您通过强制转换来强制使用所需的方法重载:

ArrayList directCopy = newArrayList((Iterator)forEnumeration(getEnumeration().getAll()));