在将应用程序升级到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);
}
}
在第一个示例中,当我首先将
UnmodifiableIterator
放入其自己的变量中,然后调用newArrayList
时,我得到了期望的结果,即将Iterators值复制到新的List
中。在第二个示例中,
forEnumeration
直接进入newArrayList
方法中,我返回了一个带有包含迭代器(包含值)的List
。根据Intellij的说法,它认为两个方法调用都应针对
newArrayList(Iterator<? extends E> elements)
,但我在调试时发现第二个调用实际上针对newArrayList(E... elements)
。仅当我使用针对Java8的Oracle JDK8进行编译时,才会发生这种情况。如果我的目标是7,则效果很好。
最佳答案
问题在于,编译器认为newArrayList(Iterator<? extends E>)
不适用(可能是由于this bug造成的),然后在不使用特定元素类型时,默默选择了始终适用的通用varargs方法(这表明存在这种重载的危险)。为您的结果列表。
该错误会显示通配符类型,即在您的代码中Attribute.getAll()
返回NamingEnumeration<?>
,因此forEnumeration
的结果是UnmodifiableIterator<?>
,编译器拒绝将其分配给Iterable<? extends E>
(newArrayList
的参数类型)。如果将内部调用的返回值强制转换为Enumeration
,问题将消失,就像将外部调用的返回值强制转换为Iterator
一样。
对于该问题,我看不到任何简单的短期解决方案。毕竟,我不明白您为什么不首先使用List<?> directCopy=Collections.list(getEnumeration().getAll());
...
请注意,如果您想查找所有出现此问题的方法,则可以仅使用已删除了newArrayList(E...)
的番石榴的修补版本,然后检查所有编译器错误(假设您没有很多情况下确实要调用此方法)超载)。重写呼叫站点后,您可以返回到原始番石榴。