usar tutorial lambdas funciones expresiones explicacion example ejemplos como java metaprogramming java-8 lambda

tutorial - java funciones lambda ejemplos



Uso explĂ­cito de LambdaMetafactory (2)

Estoy intentando usar explícitamente LambdaMetafactory.metafactory, no puedo entender por qué funciona solo con la interfaz funcional de Runnable. Por ejemplo, este código hace lo que se espera (imprime "hello world"):

public class MetafactoryTest { public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(void.class); MethodType invokedType = MethodType.methodType(Runnable.class); CallSite site = LambdaMetafactory.metafactory(caller, "run", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", methodType), methodType); MethodHandle factory = site.getTarget(); Runnable r = (Runnable) factory.invoke(); r.run(); } private static void print() { System.out.println("hello world"); } }

El problema surge cuando trato de usar una interfaz funcional diferente, como el Proveedor. El siguiente código no funciona:

public class MetafactoryTest { public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(String.class); MethodType invokedType = MethodType.methodType(Supplier.class); CallSite site = LambdaMetafactory.metafactory(caller, "get", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", methodType), methodType); MethodHandle factory = site.getTarget(); Supplier<String> r = (Supplier<String>) factory.invoke(); System.out.println(r.get()); } private static String print() { return "hello world"; } } Exception in thread "main" java.lang.AbstractMethodError: metafactorytest.MetafactoryTest$$Lambda$1/258952499.get()Ljava/lang/Object; at metafactorytest.MetafactoryTest.main(MetafactoryTest.java:29)

¿No deberían los dos fragmentos de código funcionar de manera similar, cuál es el problema en el segundo fragmento de código?

Además, el siguiente código, que debería ser equivalente, funciona bien:

public class MetafactoryTest { public static void main(String[] args) throws Throwable { Supplier<String> r = (Supplier<String>) () -> print(); System.out.println(r.get()); } private static String print() { return "hello world"; } }

Editar

Otra solución que evita cambiar el tipo de devolución del método es definir una nueva interfaz funcional:

public class MetafactoryTest { @FunctionalInterface public interface Test { String getString(); } public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(String.class); MethodType invokedType = MethodType.methodType(Test.class); CallSite site = LambdaMetafactory.metafactory(caller, "getString", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", methodType), methodType); MethodHandle factory = site.getTarget(); Test r = (Test) factory.invoke(); System.out.println(r.getString()); } private static String print() { return "hello world"; }


Este es otro ejemplo con nombres de variables más fáciles de entender:

public class Demo { public static void main(String[] args) throws Throwable { Consumer<String> consumer = s -> System.out.println("CONSUMED: " + s + "."); consumer.accept("foo"); MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType lambdaBodyMethodType = MethodType.methodType(void.class, String.class); MethodHandle lambdaBody = caller.findStatic( Demo.class, "my$lambda$main$0", lambdaBodyMethodType); // Because of the type erasure we must use Object here // instead of String (Consumer<String> -> Consumer). MethodType functionalInterfaceMethodType = MethodType.methodType(void.class, Object.class); // we must return consumer, no closure -> no additional parameters MethodType callSiteType = MethodType.methodType(Consumer.class); CallSite site = LambdaMetafactory.metafactory( // provided by invokedynamic: caller, "accept", callSiteType, // additional bootstrap method arguments: functionalInterfaceMethodType, lambdaBody, lambdaBodyMethodType); MethodHandle factory = site.getTarget(); Consumer<String> r = (Consumer<String>) factory.invoke(); r.accept("foo"); r.accept("bar"); } private static void my$lambda$main$0(String s) { System.out.println("CONSUMED: " + s + "."); } }

Debido a que LambdaMetafactory crea una clase de fábrica sintética que luego se usa para crear una interfaz de destino, callSiteType tiene un tipo de este método factory create() . Este método create() se llama implícitamente por invokedynamic . LambdaMetafactory devuelve un CallSite que tiene una referencia de método al método create. Para las lambdas con cierres, llamarás a la fábrica como factory.create(capturedValue1, ..., capturedValueN) y entonces debes modificar callSiteType consecuencia.


La diferencia entre Runnable y el Proveedor es que el Proveedor usa un tipo genérico.

En tiempo de ejecución, el proveedor no tiene un método String get (), tiene Object get (). Pero el método que implementa devuelve un String. Necesitas distinguir entre esos 2 tipos. Me gusta esto:

public class MetafactoryTest { public static void main(String[] args) throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType methodType = MethodType.methodType(Object.class); MethodType actualMethodType = MethodType.methodType(String.class); MethodType invokedType = MethodType.methodType(Supplier.class); CallSite site = LambdaMetafactory.metafactory(caller, "get", invokedType, methodType, caller.findStatic(MetafactoryTest.class, "print", actualMethodType), methodType); MethodHandle factory = site.getTarget(); Supplier<String> r = (Supplier<String>) factory.invoke(); System.out.println(r.get()); } private static String print() { return "hello world"; } }