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

时间:2021-06-22 20:47:59

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()));