with not interfaces functional example java java-8 method-reference

java - not - stream supplier



Cast java.util.function.Function a la interfaz (3)

Ejemplo

En este ejemplo (simplificado) puedo crear mi objeto MyInterface utilizando una referencia de método para apply , pero la conversión no funciona directamente.

@Test public void testInterfaceCast(){ Function<String, Integer> func = Integer::parseInt; MyInterface legal = func::apply; // works MyInterface illegal = func; // error } public interface MyInterface extends Function<String, Integer>{}

La segunda asignación da el error del compilador:

incompatible types: Function<String,Integer> cannot be converted to MyInterface

La pregunta

¿Puedo hacer algo de magia genérica para poder lanzar una Function<T, R> a una interfaz?


En el fondo de esto está el hecho de que, a pesar de toda la sintaxis bonita que da tal apariencia superficialmente, Java no ha adoptado tipos estructurales; Las reglas de la sustituibilidad de Liskov de los tipos nombrados se mantienen tan estrictamente como siempre. En tu código de la vida real, llamas Function.andThen , que devuelve alguna instancia de Function , y definitivamente no es una instancia de MyInterface . Así que cuando escribes

MyInterface legal = func::apply;

obtendrás otro objeto, que es una instancia de MyInterface . Por el contrario, cuando escribes

MyInterface illegal = func;

está intentando asignar una referencia a la instancia existente de Function directamente como illegal , lo que violaría la posibilidad de sustitución de Liskov aunque coincida estructuralmente con el tipo. Ningún truco puede existir para solucionar esto fundamentalmente; incluso si Java introdujera algo de azúcar sintáctico nuevo que lo hiciera parecer un casting, las semánticas reales seguirían siendo las de conversión (evaluando a otra instancia).


La razón MyInterface illegal = func; no funciona, es porque func se declara como una variable de tipo Function<String,Integer> , que no es un subtipo de MyInterface .

La razón por la que MyInterface legal = func::apply; El trabajo es el siguiente. Podría esperar que el tipo fun::apply sea ​​también Function<String,Integer> , pero este no es el caso. El tipo de fun::apply depende en parte de qué tipo espera el compilador. Como lo usa en un contexto de asignación, el compilador espera una expresión de tipo MyInterface y, por lo tanto, en ese contexto, func::apply es de tipo MyInterface . Es por este motivo que la expresión de referencia del método solo puede aparecer en contextos de asignación, contextos de invocación y contextos de conversión (consulte la Especificación del lenguaje Java ).

Como la Function<String,Integer> no es un subtipo de MyInterface , la func conversión a MyInterface lanza una MyInterface ClassCastException . Por lo tanto, la forma más clara de convertir una Function<String,Integer> a un MyInterface es usar una referencia de método, como ya hizo:

MyInterface legal = func::apply;


No se compila por la misma razón que este fragmento de código no se compila:

Animal animal = new Animal(); Cat x = animal; //illegal assignment

es decir, estás insinuando que cada animal puede ser un gato. El compilador no tiene pruebas de que la instancia de la superclase sea realmente un gato en tiempo de ejecución y, por lo tanto, genere un error.

MyInterface asignar Integer::parseInt a una instancia de MyInterface :

MyInterface func = Integer::parseInt; MyInterface illegal = func;

como func es de tipo Function<String, Integer> (es decir, el MyInterface de MyInterface ).

La verdad es que no hay forma de hacer Integer::parseInt de tipo MyInterface en tiempo de compilación, a menos que se lo asigne a la variable MyInterface .