While upgrading an app to Java 8 I ran into a weird issue with google guava's newArrayList
in a couple of places.
在将应用程序升级到Java 8时,我在几个地方遇到了google guava的newArrayList这个奇怪的问题。
Take a look at this example:
看看这个例子:
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);
}
}
In the first example when I get the UnmodifiableIterator
first into its own variable and then call newArrayList
I get what I expect, which is the Iterators values copied into a new List
.
在第一个例子中,当我首先将UnmodifiableIterator放入其自己的变量然后调用newArrayList时,我得到了我所期望的,即Iterators值被复制到新的List中。
In the second example where the forEnumeration
goes directly into the newArrayList
method I get back a List
with a containing the iterator (which contains the value).
在forEnumeration直接进入newArrayList方法的第二个例子中,我得到一个包含迭代器(包含值)的List。
According to Intellij it thinks that both method calls should be to newArrayList(Iterator<? extends E> elements)
but I found when debugging that the second call actually goes to newArrayList(E... elements)
.
根据Intellij的说法,它认为两个方法调用都应该是newArrayList(Iterator elements)但是我在调试时发现第二个调用实际上是newArrayList(E ... elements)。
It only happens when I compile with the Oracle JDK8 targeted to Java8. If I target to 7 it works fine.
只有当我使用针对Java8的Oracle JDK8进行编译时才会发生这种情况。如果我的目标是7,它可以正常工作。
2 个解决方案
#1
7
The problem is that the compiler thinks that newArrayList(Iterator<? extends E>)
is not applicable (maybe because of this bug) and then silently chooses the generic varargs method which is always applicable (which shows the danger of such overload), when you don’t use a specific element type for your result list.
问题是编译器认为newArrayList(Iterator )不适用(可能是因为这个bug),然后默默地选择始终适用的通用varargs方法(显示这种重载的危险),当您不要为结果列表使用特定的元素类型。
The bug appears with wildcard types, i.e. in your code it’s Attribute.getAll()
returning a NamingEnumeration<?>
hence the result of forEnumeration
is UnmodifiableIterator<?>
which the compiler refuses to assign to Iterable<? extends E>
, the parameter type of newArrayList
. If you cast the return value of the inner call to Enumeration
the problem disappears as it does when you cast the return value of the outer call to Iterator
.
该错误出现在通配符类型中,即在您的代码中,Attribute.getAll()返回NamingEnumeration ,因此forEnumeration的结果是UnmodifiableIterator ,编译器拒绝将其分配给Iterable ,newArrayList的参数类型。如果将内部调用的返回值强制转换为Enumeration,则问题将消失,就像将外部调用的返回值强制转换为Iterator时一样。
I don’t see a simple short-term solution for this problem. After all, I don’t understand why you didn’t use List<?> directCopy=Collections.list(getEnumeration().getAll());
in the first place…
我没有看到这个问题的简单短期解决方案。毕竟,我不明白为什么你没有使用List directCopy = Collections.list(getEnumeration()。getAll());首先…
Note that if you want to find all occurrences of this problem, you may simply use a patched version of guava in which newArrayList(E...)
has been removed and check all compiler-errors (assuming you don’t have many case where you really want to call this overload). After rewriting the call sites, you may turn back to the original guava.
请注意,如果要查找此问题的所有实例,您可以简单地使用已删除newArrayList(E ...)的guava的修补版本并检查所有编译器错误(假设您没有很多情况,其中你真的想叫这个超载)。重写呼叫站点后,您可以返回原始番石榴。
#2
1
I've seen this happen with overloaded methods and generic types. In this case the more generic version of newArrayList()
is being chosen when the parameter is not explicitly typed.
我已经看到这种情况发生在重载方法和泛型类型上。在这种情况下,当未明确键入参数时,将选择更通用的newArrayList()版本。
I don't have a technical explanation for you, but I'd recommend you force usage of the desired method overload by casting:
我没有为您提供技术解释,但我建议您通过强制转换强制使用所需的方法重载:
ArrayList directCopy = newArrayList((Iterator)forEnumeration(getEnumeration().getAll()));
#1
7
The problem is that the compiler thinks that newArrayList(Iterator<? extends E>)
is not applicable (maybe because of this bug) and then silently chooses the generic varargs method which is always applicable (which shows the danger of such overload), when you don’t use a specific element type for your result list.
问题是编译器认为newArrayList(Iterator )不适用(可能是因为这个bug),然后默默地选择始终适用的通用varargs方法(显示这种重载的危险),当您不要为结果列表使用特定的元素类型。
The bug appears with wildcard types, i.e. in your code it’s Attribute.getAll()
returning a NamingEnumeration<?>
hence the result of forEnumeration
is UnmodifiableIterator<?>
which the compiler refuses to assign to Iterable<? extends E>
, the parameter type of newArrayList
. If you cast the return value of the inner call to Enumeration
the problem disappears as it does when you cast the return value of the outer call to Iterator
.
该错误出现在通配符类型中,即在您的代码中,Attribute.getAll()返回NamingEnumeration ,因此forEnumeration的结果是UnmodifiableIterator ,编译器拒绝将其分配给Iterable ,newArrayList的参数类型。如果将内部调用的返回值强制转换为Enumeration,则问题将消失,就像将外部调用的返回值强制转换为Iterator时一样。
I don’t see a simple short-term solution for this problem. After all, I don’t understand why you didn’t use List<?> directCopy=Collections.list(getEnumeration().getAll());
in the first place…
我没有看到这个问题的简单短期解决方案。毕竟,我不明白为什么你没有使用List directCopy = Collections.list(getEnumeration()。getAll());首先…
Note that if you want to find all occurrences of this problem, you may simply use a patched version of guava in which newArrayList(E...)
has been removed and check all compiler-errors (assuming you don’t have many case where you really want to call this overload). After rewriting the call sites, you may turn back to the original guava.
请注意,如果要查找此问题的所有实例,您可以简单地使用已删除newArrayList(E ...)的guava的修补版本并检查所有编译器错误(假设您没有很多情况,其中你真的想叫这个超载)。重写呼叫站点后,您可以返回原始番石榴。
#2
1
I've seen this happen with overloaded methods and generic types. In this case the more generic version of newArrayList()
is being chosen when the parameter is not explicitly typed.
我已经看到这种情况发生在重载方法和泛型类型上。在这种情况下,当未明确键入参数时,将选择更通用的newArrayList()版本。
I don't have a technical explanation for you, but I'd recommend you force usage of the desired method overload by casting:
我没有为您提供技术解释,但我建议您通过强制转换强制使用所需的方法重载:
ArrayList directCopy = newArrayList((Iterator)forEnumeration(getEnumeration().getAll()));