Métodos de acceso de Java con LambdaMetafactory

mi pregunta está fuertemente relacionada con el uso explícito de LambdaMetafactory en ese hilo, se proporcionan algunos muy buenos ejemplos para usar el LambdaMetafactory para acceder a un método estático de una clase; sin embargo, me pregunto cuál es el código equivalente para acceder a un campo no estático de una instancia de bean existente. Parece realmente difícil encontrar un ejemplo y cada bash que realicé terminó en un código no funcional.

Este es el código de frijol:

class SimpleBean { private Object obj= "myCustomObject"; private static Object STATIC_OBJECT = "myCustomStaticObject"; public Object getObj() { return obj; } public void setObj(final Object obj) { this.obj = obj; } public static Object getStaticObj() { return STATIC_OBJECT; } public static void setStaticObj(final Object obj) { STATIC_OBJECT = obj; } } 

Aquí una prueba de unidad de trabajo que accedió con éxito al método estático “getStaticObj ()”:

  @Test public void accessStaticMethod() throws Throwable { MethodHandles.Lookup caller = MethodHandles.lookup(); Method reflected = SimpleBean.class.getDeclaredMethod("getStaticObj"); MethodHandle methodHandle = caller.unreflect(reflected); CallSite site = LambdaMetafactory.metafactory(caller, "get", MethodType.methodType(Supplier.class), MethodType.methodType(Object.class), methodHandle, MethodType.methodType(Object.class)); MethodHandle factory = site.getTarget(); Supplier r = (Supplier) factory.invoke(); assertEquals( "myCustomStaticObject", r.get()); } 

Ahora aquí mis bashs fallidos para acceder al método “getObj ()” no estático:

  @Test public void accessNonStaticMethodTestOne() throws Throwable { SimpleBean simpleBeanInstance = new SimpleBean(); MethodHandles.Lookup caller = MethodHandles.lookup(); MethodHandle methodHandle = caller.bind(simpleBeanInstance, "getObj", MethodType.methodType(Object.class)); assertEquals("myCustomObject", methodHandle.invoke()); // This test fails here with exception: // java.lang.IllegalArgumentException: not a direct method handle CallSite site = LambdaMetafactory.metafactory(caller, "get", MethodType.methodType(Supplier.class), MethodType.methodType(Object.class), methodHandle, MethodType.methodType(Object.class)); MethodHandle factory = site.getTarget(); Supplier r = (Supplier) factory.invoke(); assertEquals( "myCustomObject", r.get()); } @Test public void accessNonStaticMethodTwo() throws Throwable { SimpleBean simpleBeanInstance = new SimpleBean(); MethodHandles.Lookup caller = MethodHandles.lookup(); Method reflected = SimpleBean.class.getDeclaredMethod("getObj"); MethodHandle methodHandle = caller.unreflect(reflected); // This test fails here with exception: // java.lang.invoke.LambdaConversionException: Incorrect number of parameters CallSite site = LambdaMetafactory.metafactory(caller, "get", MethodType.methodType(Supplier.class), MethodType.methodType(Object.class), methodHandle, MethodType.methodType(Object.class)); MethodHandle factory = site.getTarget(); factory = factory.bindTo(simpleBeanInstance); Supplier r = (Supplier) factory.invoke(); assertEquals( "myCustomObject", r.get()); } @Test public void accessNonStaticMethodThree() throws Throwable { SimpleBean simpleBeanInstance = new SimpleBean(); MethodHandles.Lookup caller = MethodHandles.lookup(); Method reflected = SimpleBean.class.getDeclaredMethod("getObj"); MethodHandle methodHandle = caller.unreflect(reflected); CallSite site = LambdaMetafactory.metafactory(caller, "get", MethodType.methodType(Supplier.class), MethodType.methodType(Object.class, SimpleBean.class), methodHandle, MethodType.methodType(Object.class, SimpleBean.class)); MethodHandle factory = site.getTarget(); //This test fails here with exception: // java.lang.IllegalArgumentException: no leading reference parameter: spike.LambdaBeanAccessAtRuntimeTest$SimpleBean@4459eb14 factory = factory.bindTo(simpleBeanInstance); Supplier r = (Supplier) factory.invoke(); assertEquals( "myCustomObject", r.get()); } 

Cada bash tiene un resultado negativo diferente, realmente espero que alguien pueda ayudarme a tener al menos una prueba que funcione bien.

Si desea vincular valores a su lamba, debe incluir estos parámetros en la firma invokedtype:

 SimpleBean simpleBeanInstance = new SimpleBean(); MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType getter=MethodType.methodType(Object.class); MethodHandle target=caller.findVirtual(SimpleBean.class, "getObj", getter); CallSite site = LambdaMetafactory.metafactory(caller, "get", // include types of the values to bind: MethodType.methodType(Supplier.class, SimpleBean.class), getter, target, getter); MethodHandle factory = site.getTarget(); factory = factory.bindTo(simpleBeanInstance); Supplier r = (Supplier) factory.invoke(); assertEquals( "myCustomObject", r.get()); 

En lugar de vincular un valor, puede usar una Function que toma el bean como argumento:

 SimpleBean simpleBeanInstance = new SimpleBean(); MethodHandles.Lookup caller = MethodHandles.lookup(); MethodType getter=MethodType.methodType(Object.class); MethodHandle target=caller.findVirtual(SimpleBean.class, "getObj", getter); MethodType func=target.type(); CallSite site = LambdaMetafactory.metafactory(caller, "apply", MethodType.methodType(Function.class), func.generic(), target, func); MethodHandle factory = site.getTarget(); Function r = (Function)factory.invoke(); assertEquals( "myCustomObject", r.apply(simpleBeanInstance));