Java 8 编译器与重载方法混淆

Java 8 Compiler Confusion With overloaded methods

在将应用程序升级到 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 时,我得到了我期望的结果,即迭代器值被复制到一个新的 List.

在第二个示例中,forEnumeration 直接进入 newArrayList 方法,我得到一个 List 和包含迭代器(包含值)的 a。

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

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

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

我没有给你的技术解释,但我建议你通过转换强制使用所需的方法重载:

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

问题是编译器认为 newArrayList(Iterator<? extends E>) 不适用(可能是因为 this bug),然后默默地选择 always[=31 的通用可变参数方法=] 适用(这显示了这种过载的危险),当您没有为结果列表使用特定的元素类型时。

错误出现在通配符类型中,即在您的代码中它是 Attribute.getAll() returning a NamingEnumeration<?> 因此 forEnumeration 的结果是 UnmodifiableIterator<?>编译器拒绝将 newArrayList 的参数类型分配给 Iterable<? extends E>。如果将内部调用的 return 值转换为 Enumeration,问题就会消失,就像将外部调用的 return 值转换为 Iterator.[=22 时一样=]

我看不到这个问题的简单短期解决方案。毕竟,我不明白你为什么不首先使用 List<?> directCopy=Collections.list(getEnumeration().getAll());...

请注意,如果你想找到所有出现的这个问题,你可以简单地使用补丁版本的番石榴,其中 newArrayList(E...) 已被删除并检查所有编译器错误(假设你没有许多情况下你真的想调用这个重载)。重写调用点后,你可以回到原来的番石榴。