使用lambdas和泛型时,对方法的引用是不明确的 [英] Reference to method is ambiguous when using lambdas and generics

查看:130
本文介绍了使用lambdas和泛型时,对方法的引用是不明确的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我收到以下代码的错误,我认为不应该存在...使用JDK 8u40编译此代码。

I am getting an error on the following code, which I believe should not be there... Using JDK 8u40 to compile this code.

public class Ambiguous {
    public static void main(String[] args) {
        consumerIntFunctionTest(data -> {
            Arrays.sort(data);
        }, int[]::new);

        consumerIntFunctionTest(Arrays::sort, int[]::new);
    }

    private static <T> void consumerIntFunctionTest(final Consumer<T> consumer, final IntFunction<T> generator) {

    }

    private static <T> void consumerIntFunctionTest(final Function<T, ?> consumer, final IntFunction<T> generator) {

    }
}

错误如下:


错误:(17,9)java :对userIntFunctionTest的引用是不明确的
net.tuis.ubench.Ambiguous中的方法consumerIntFunctionTest(java.util.function.Consumer,java.util.function.IntFunction)和方法consumerIntFunctionTest(java.util.function.Function, net.tuis.ubench.Ambiguous match中的java.util.function.IntFunction)

Error:(17, 9) java: reference to consumerIntFunctionTest is ambiguous both method consumerIntFunctionTest(java.util.function.Consumer,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous and method consumerIntFunctionTest(java.util.function.Function,java.util.function.IntFunction) in net.tuis.ubench.Ambiguous match

错误发生在以下行:

consumerIntFunctionTest(Arrays::sort, int[]::new);

我认为应该没有错误,因为所有 Arrays :: sort 引用的类型为 void ,并且它们都不返回值。正如您所观察到的,当我明确地扩展 Consumer< T> lambda时, 工作。

I believe there should be no error, as all Arrays::sort references are of type void, and none of them return a value. As you can observe, it does work when I explicitly expand the Consumer<T> lambda.

这真的是javac中的一个错误,还是JLS声明lambda在这种情况下无法自动扩展?如果是后者,我仍然认为这很奇怪,因为 consumerIntFunctionTest 作为第一个参数功能< T,?> 不应该匹配。

Is this really a bug in javac, or does the JLS state that the lambda cannot automatically be expanded in this case? If it is the latter, I would still think it is weird, as consumerIntFunctionTest with as first argument Function<T, ?> should not match.

推荐答案

在你的第一个例子中

consumerIntFunctionTest(data -> {
        Arrays.sort(data);
    }, int[]::new);

lambda表达式有一个 void -compatible可以通过表达式结构识别的块而无需解析实际类型。

the lambda expression has a void-compatible block which can be identified by the structure of the expression without the need to resolve the actual types.

相比之下,在示例中

consumerIntFunctionTest(Arrays::sort, int[]::new);

必须解析方法参考,以确定是否符合 void function( Consumer )或值返回函数( Function )。这同样适用于简化的lambda表达式

the method reference has to be resolved to find out, whether it conforms to either, a void function (Consumer) or a value returning function (Function). The same applies to the simplified lambda expression

consumerIntFunctionTest(data -> Arrays.sort(data), int[]::new);

可能是两者, void - 兼容或者价值兼容,取决于已解决的目标方法。

which could be both, void- compatible or value- compatible, depending on the resolved target method.

问题在于解析方法需要了解所需的签名,这应该通过目标类型来确定,但是,在知道泛型方法的类型参数之前,目标类型是未知的。虽然理论上两者都可以立即确定,但在规范中已经简化了(仍然非常复杂)过程,该方法首先执行重载决策,最后应用类型推断(参见JLS§15.12.2)。因此,类型推断可以提供的信息不能用于解决重载决策。

The problem is that resolving the method requires knowledge about the required signature, which ought to be determined via target typing, but the target type isn’t known until the type parameters of the generic method are known. While in theory both could be determined at once, the (still being awfully complex) process has been simplified in the specification in that method overload resolution is performed first and type inference is applied last (see JLS §15.12.2). Hence, the information that type inference could provide cannot be used for solving overload resolution.

但请注意 15.12.2.1。确定可能适用的方法包含:


表达式可能与具有目标类型根据以下规则:

An expression is potentially compatible with a target type according to the following rules:


  • lambda表达式(§15.27)可能与功能接口类型(§9.8)兼容以下是真实的:

  • A lambda expression (§15.27) is potentially compatible with a functional interface type (§9.8) if all of the following are true:


  • 目标类型的函数类型的arity与lambda表达式的arity相同。

  • The arity of the target type's function type is the same as the arity of the lambda expression.

如果目标类型的函数类型具有void返回,则lambda主体是语句表达式(§14.8)或void兼容块(§) 15.27.2)。

If the target type's function type has a void return, then the lambda body is either a statement expression (§14.8) or a void-compatible block (§15.27.2).

如果目标类型的函数类型具有(非void)返回类型,则lambda主体是表达式或值兼容的块(第15.27.2节)。

If the target type's function type has a (non-void) return type, then the lambda body is either an expression or a value-compatible block (§15.27.2).

方法引用表达式(第15.13节)是潜在的与功能接口类型兼容,如果,在类型的函数类型arity为n的情况下,对于具有arity n(第15.13.1节)的方法引用表达式,至少存在一个可能适用的方法,并且以下之一为真:

A method reference expression (§15.13) is potentially compatible with a functional interface type if, where the type's function type arity is n, there exists at least one potentially applicable method for the method reference expression with arity n (§15.13.1), and one of the following is true:


  • 方法引用表达式的形式为ReferenceType :: [TypeArguments]标识符,并且至少一个可能适用的方法是i)static并支持arity n,或ii)not static并且支持arity n-1。

  • The method reference expression has the form ReferenceType :: [TypeArguments] Identifier and at least one potentially applicable method is i) static and supports arity n, or ii) not static and supports arity n-1.

方法引用表达式具有其他形式,并且至少一个可能适用的方法不是静态的。

The method reference expression has some other form and at least one potentially applicable method is not static.

...

< sup> 潜在适用性的定义超出了基本的arity检查,也考虑了功能接口目标类型的存在和形状。在某些涉及类型参数推断的情况下,出现作为方法调用参数的lambda表达式在重载解析之后才能正确键入

所以你的第一个例子中的一个方法是由lambda的形状整理出来的,而在方法引用或lambda表达式由一个单独的调用表达式组成的情况下,两个可能适用的方法都能忍受这个第一个选择过程并且在类型推断可以启动之前产生模糊错误以帮助找到目标方法以确定它是否是 void 或值返回方法。

So your in first example one of the methods is sorted out by the lambda’s shape while in case of a method reference or a lambda expression consisting of a sole invocation expression, both potentially applicable methods endure this first selection process and yield an "ambiguous" error before type inference can kick in to aid finding a target method to determine if it’s a void or value returning method.

请注意,例如使用 x-> {foo(); } 显式生成lambda表达式 void -compatible,你可以使用 x->(foo())使lambda表达式显式地与值兼容。

Note that like using x->{ foo(); } to make a lambda expression explicitly void-compatible, you can use x->( foo() ) to make a lambda expression explicitly value-compatible.

你疯狂进一步阅读这个答案解释了组合类型推断和方法重载决策的这种限制是一个刻意(但不容易)的决定。

You mad further read this answer explaining that this limitation of combined type inference and method overload resolution was a deliberate (but not easy) decision.

这篇关于使用lambdas和泛型时,对方法的引用是不明确的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆