para interfaces instrucciones functionalinterface funciones funcionales expresiones example anonimas java lambda functional-programming java-8

interfaces - Método ambiguo Java 8 lambda para interfaz funcional-Tipo de objetivo



interfaces funcionales java (1)

Tengo el siguiente código:

public class LambdaTest1 { public static void method1(Predicate<Integer> predicate){ System.out.println("Inside Predicate"); } public static void method1(Function<Integer,String> function){ System.out.println("Inside Function"); } public static void main(String[] args) { method1((i) -> "Test"); } }

Esto me está dando un mensaje de error como

"El método method1 (predicado) es ambiguo para el tipo LambdaTest1".

Puedo ver que para Consumer interfaz funcional Function y Consumer , el argumento de entrada es Integer . Pero para la Function , el tipo de devolución es String . Como mi llamada tiene un tipo de devolución como "Texto". Esto debería haber llamado a mi interfaz funcional Function en lugar de arrojar este error.

¿Alguien puede explicar por qué este comportamiento?

También otro ejemplo:

public class LambdaTest1 { public static void method1(Consumer<Integer> consumer){ System.out.println("Inside Consumer"); } public static void method1(Predicate<Integer> predicate){ System.out.println("Inside Predicate"); } public static void main(String[] args) { List<Integer> lst = new ArrayList<Integer>(); method1(i -> true); method1(s -> lst.add(s)); //ambiguous error } }

También en el código anterior la línea method1 (s -> lst.add (s)); da un error ambiguo pero donde como el método de la línea de arriba1 (i -> verdadero) funciona bien.


Como se explica en esta respuesta , los diseñadores de lenguaje de Java hicieron un corte deliberado cuando se trata del proceso de selección de un método sobrecargado en combinación con la inferencia de tipo. Por lo tanto, no todos los aspectos de un parámetro de expresión lambda se utilizan para determinar el método correcto sobrecargado.

En particular, en su primer ejemplo, la expresión lambda (i) -> "Test" es una expresión lambda implícitamente tipada cuyos valores de retorno no se consideran para la resolución de sobrecarga, mientras que cambian a, por ejemplo, (Integer i) -> "Test" lo convertirá en una expresión lambda explícitamente tipada cuyos valores de retorno se consideran. Compare con la Especificación del lenguaje Java §15.12.2.2. :

Fase 1: Identificar los métodos de Arity coincidentes aplicables mediante la invocación estricta

Una expresión de argumento se considera pertinente a la aplicabilidad de un método potencialmente aplicable m menos que tenga una de las siguientes formas:

  • Una expresión lambda implícitamente tipada (§15.27.1).

...

  • Una expresión lambda explícitamente tipada cuyo cuerpo es una expresión que no es pertinente para la aplicabilidad.

  • Una expresión lambda explícitamente tipada cuyo cuerpo es un bloque, donde al menos una expresión de resultado no es pertinente a la aplicabilidad.

...

Así que las expresiones lambda explícitamente tipadas pueden ser "pertinentes a la aplicabilidad", dependiendo de su contenido, mientras que las implícitamente tipadas se descartan en general. También hay un apéndice, que es aún más específico:

El significado de una expresión lambda implícitamente tipada o una expresión de referencia de método inexacta es suficientemente vago antes de resolver un tipo de destino que los argumentos que contienen estas expresiones no se consideran pertinentes a la aplicabilidad; simplemente se ignoran (excepto por la aridez esperada) hasta que finaliza la resolución de sobrecarga.

Por lo tanto, usar el tipo implícitamente tipeado (i) -> "Test" no ayuda a decidir si invocar method1(Predicate<Integer>) o method1(Function<Integer,String>) y dado que ninguno es más específico, la selección del método falla antes de intentar inferir el tipo de función de la expresión lambda.

El otro caso, seleccionar entre method1(Consumer<Integer>) y method1(Predicate<Integer>) es diferente, ya que el parámetro de un método tiene un tipo de función con un retorno void y el otro es un tipo de retorno no void , que permite seleccionar un método aplicable a través de la forma de la expresión lambda, que ya se ha discutido en la respuesta vinculada . i -> true solo es compatible con el valor , por lo tanto, inapropiado para un Consumer . Del mismo modo, i -> {} solo es compatible con el vacío , por lo tanto, es inapropiado para un Predicate .

Solo hay unos pocos casos en que la forma es ambigua:

  • cuando el bloque nunca se completa normalmente, por ejemplo, arg -> { throw new Exception(); } arg -> { throw new Exception(); } o
    arg -> { for(;;); }
  • cuando la expresión lambda tiene la forma arg -> expression y expression es también una declaración. Tales declaraciones de expresión son
    • Asignaciones, por ejemplo, arg -> foo=arg
    • Expresiones de incremento / decremento, por ej. arg -> counter++
    • Invocaciones de método, como en su ejemplo s -> lst.add(s)
    • Instanciaciones, por ejemplo, arg -> new Foo(arg)

Tenga en cuenta que las expresiones entre paréntesis no están dentro de esta lista, por lo tanto, cambie s -> lst.add(s) para
s -> (lst.add(s)) es suficiente para convertirlo en una expresión que ya no es compatible con el vacío. Del mismo modo, convertirlo en una declaración como s -> {lst.add(s);} evita que sea compatible con el valor. Por lo tanto, es fácil seleccionar el método correcto en este escenario.