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...)
已被删除并检查所有编译器错误(假设你没有许多情况下你真的想调用这个重载)。重写调用点后,你可以回到原来的番石榴。
在将应用程序升级到 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...)
已被删除并检查所有编译器错误(假设你没有许多情况下你真的想调用这个重载)。重写调用点后,你可以回到原来的番石榴。