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

ryb*_*ber 7 java guava java-8

在将应用程序升级到Java 8时,我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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在第一个例子中,当我得到第UnmodifiableIterator一个进入它自己的变量,然后调用newArrayList我得到我期望的,这是Iterators值复制到一个新的List.

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

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

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

Hol*_*ger 7

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

该漏洞出现通配符类型,即在你的代码它Attribute.getAll()返回NamingEnumeration<?>的,因此其结果forEnumerationUnmodifiableIterator<?>,编译器拒绝分配Iterable<? extends E>,参数类型的newArrayList.如果将内部调用的返回值强制转换Enumeration为问题,则将外部调用的返回值转换为Iterator.

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

请注意,如果要查找此问题的newArrayList(E...)所有实例,您可以简单地使用已删除的guava的修补版本并检查所有编译器错误(假设您没有很多情况下您真的想要调用此重载).重写呼叫站点后,您可以返回原始番石榴.

  • 似乎是一个版本问题.刚用`1.8.0_20`测试它,它产生了一个错误和一个beta`1.8.0_40`编译.这是一致的:当使用`1.8.0_40`编译而没有删除varargs方法时,它仍然会调用正确的方法. (2认同)