java reflection java-7

java - Algunas preguntas básicas sobre MethodHandle API



reflection java-7 (3)

Cuál es la diferencia entre MethodHandle.invoke (), MethodHandle.invokeExact () y MethodHandle.invokeWithArguments ()

Como también tuve problemas con esto, decidí volver a examinar esta pregunta y escribir un ejemplo que muestre exactamente cuáles son las diferencias semánticas entre estos métodos.

Las principales diferencias son:

  1. invokeExact solo acepta argumentos exactos y tipos de retorno, y no acepta argumentos como una matriz. Llamar, por ejemplo, firma de método (Integer,Integer)Integer no se permite el (Integer,Integer)Integer con un argumento int , pero tampoco se permite llamarlo con un argumento Object , incluso si el objeto es en realidad de tipo Integer: el tipo compiletime del argumento debe ser de clase Entero, no la instancia de tiempo de ejecución:

    Object arg = 1; Object[] args = {1,1}; Integer i = (Integer)handle.invokeExact(1,1); // OK Object o = handle.invokeExact(arg,arg); // ERROR handle.invokeExact(args); // ERROR

  1. invoke convierte automáticamente los tipos de argumentos y tipos de retorno y también convierte entre los tipos primitivos y las clases de envoltorio correspondientes. Pero tampoco acepta argumentos como una matriz. Por ejemplo, para la firma del método (Integer,Integer)Integer :

    Object arg = 1; Object[] args = {1,1}; Integer i = (Integer)handle.invoke(1,1); // OK Object o = handle.invoke(arg,arg); // OK! o = handle.invoke(args); // ERROR

  1. invokeWithArguments elimina todas estas restricciones y funciona de manera muy similar al Method#invoke : también puede proporcionar una matriz (o java.util.List ) como argumento (pero esta flexibilidad viene con una gran penalización de rendimiento). Por ejemplo, para la firma del método (Integer,Integer)Integer :

    Object arg = 1; Object[] args = {1,1}; Integer i = (Integer)handle.invokeWithArguments(1,1); // OK Object o = handle.invokeWithArguments(arg,arg); // OK o = handle.invokeWithArguments(args); // OK!

Aquí un ejemplo completo:

import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.WrongMethodTypeException; import java.lang.reflect.Method; import java.util.Arrays; public class MethodHandleTest { public static class TestClass{ public int test(int a, Integer b){ return a+b; } } public static void main(String[] args) throws Throwable{ Method method = TestClass.class.getMethod("test", int.class, Integer.class); MethodHandle handle = MethodHandles.lookup().unreflect(method).bindTo(new TestClass()); int arg_int = 1; Integer argInteger = 1; Object[] argArray = {1,1}; //---------------------------- // MethodHandle#invokeExact() //---------------------------- // ONLY an exact invocation is allowed for invokeExact: int result = (int) handle.invokeExact(arg_int, argInteger); // inexact first argument type -> throws WrongMethodTypeException - "expected (int,Integer)int but found (Integer,Integer)int" Exception e = null; try { result = (int) handle.invokeExact(argInteger,argInteger); } catch (WrongMethodTypeException ex) { e = ex; } assert e != null; e = null; // inexact return type -> throws WrongMethodTypeException - "expected (int,Integer)int but found (int,Integer)Integer" try { result = (Integer) handle.invokeExact(arg_int,argInteger); } catch (WrongMethodTypeException ex) { e = ex; } assert e != null; e = null; // inexact return type -> throws WrongMethodTypeException - "expected (int,Integer)int but found (int,Integer)Object" try { Object o = handle.invokeExact(arg_int,argInteger); } catch (WrongMethodTypeException ex) { e = ex; } assert e != null; e = null; // "argObject" is ALSO NOT OK! - the compile time type of the argument must be of class Integer, not the runtime instance! // -> throws WrongMethodTypeException - "expected (int,Integer)int but found (int,Object)int" Object argObject = argInteger; try { result = (int) handle.invokeExact(arg_int,argObject); } catch (WrongMethodTypeException ex) { e = ex; } assert e != null; e = null; // Array of the arguments NOT allowed -> throws WrongMethodTypeException - "expected (int,Integer)int but found (Object[])int" try { result = (int) handle.invokeExact(argArray); } catch (WrongMethodTypeException ex) { e = ex; } assert e != null; e = null; // But explicit cast of first or second argument is OK result = (int) handle.invokeExact((int)argInteger,argInteger); result = (int) handle.invokeExact(arg_int,(Integer)arg_int); //----------------------- // MethodHandle#invoke() //----------------------- // invoke() with exact types - OK -> actually calls invokeExact() behind the scenes result = (int) handle.invoke(arg_int, argInteger); // implicit conversion of inexact arguments and return type -> OK! result = (Integer) handle.invoke(argInteger,argInteger); // Object arguments or return type is OK! Object o = handle.invoke(argObject,argObject); // Array of the arguments NOT allowed -> throws WrongMethodTypeException - "cannot convert MethodHandle(int,Integer)int to (Object[])int" try { result = (int) handle.invoke(argArray); } catch (WrongMethodTypeException ex) { e = ex; } assert e != null; e = null; //------------------------------------ // MethodHandle#invokeWithArguments() //------------------------------------ // invoke() with exact types - OK result = (int) handle.invokeWithArguments(arg_int,arg_int); // implicit conversion of inexact arguments and return type -> OK result = (Integer) handle.invokeWithArguments(argInteger,argInteger); // Object arguments or return type is OK! o = handle.invoke(argObject,argObject); // Array of the arguments -> OK result = (int) handle.invokeWithArguments(argArray); // List of arguments possible -> same as calling invokeWithArguments(list.toArray()) result = (int) handle.invokeWithArguments(Arrays.asList(argArray)); } }

¿Cómo puedo obtener todos los métodos declarados a través de MethodHandles.lookup() ? ¿Cómo puedo obtener todos los campos declarados?

Cuál es la diferencia entre MethodHandle.invoke() , MethodHandle.invokeExact() y MethodHandle.invokeWithArguments()

Además, apreciaré el tutorial sobre el uso de la API MethodHandle para desarrolladores de Java . Hago hincapié en que estoy programando en Java de lenguaje simple tipificado estáticamente y no soy desarrollador de JVM, en particular no me interesa la basura de código de bytes completo (invokedynamic). Quiero descubrir cómo puedo usar esta nueva API en lugar de la API Java Core.

EDITADO-2:

A continuación, @Glen Best proporcionó algunas referencias. Deseo proporcionar solo una http://www.oraclejavamagazine-digital.com/javamagazine/20130102?pg=52&search_term=methodhandle&doc_id=-1#pg50 Esto es exactamente lo que estaba buscando. Me di cuenta de que en realidad hay un nuevo vocabulario. Por ejemplo, por objetivo se entiende MethodHandle (y no el objeto sobre el que se realiza el envío) y el sitio de llamadas es en realidad un código que "invoca" el puntero de función "aka MethodHandle. También es esencial comprender que la API MethodHandle no reemplaza a la API Core Reflection en lugar de suplementarla . Por ejemplo, no puede "descubrir" todos los métodos con MethodHandle que necesita Core Reflection API. Pero cuando "encuentra" el método deseado, puede cambiar a MethodHandle y, por ejemplo, enlazar algunos de sus parámetros o "cambiar" (adaptar) su firma a varargs, por ejemplo.

EDITADO:

Todavía estoy tratando de averiguar la respuesta. Escribí algunas pruebas que quiero compartir con todos.

package alexander.berkovich; import static org.junit.Assert.assertSame; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Field; import java.lang.reflect.Method; import org.junit.BeforeClass; import org.junit.Test; public class MethodHandlerCleanTest { public static MethodHandles.Lookup lookup; @BeforeClass public static void asetUp(){ lookup = MethodHandles.lookup(); } public static class Check { public void primitive(final int i){ } public void wrapper(final Integer i){ } } @Test public void testPrimitive() throws Throwable { Check check = new Check(); MethodType type = MethodType.methodType(void.class, int.class); MethodHandle mh = lookup.findVirtual(Check.class, "primitive", type); mh.invokeWithArguments(check, 1); mh.invoke(check, (short)2); mh.invoke(check, Integer.valueOf(3)); Method method = Check.class.getMethod("primitive", int.class); method.invoke(check, (short)20); method.invoke(check, Integer.valueOf(21)); } @Test public void testWrapper() throws Throwable { Check check = new Check(); MethodType type = MethodType.methodType(void.class, Integer.class); MethodHandle mh = lookup.findVirtual(Check.class, "wrapper", type); mh.invoke(check, 2); Method method = Check.class.getMethod("wrapper", Integer.class); method.invoke(check, 20); } @SuppressWarnings("unused") public static class StaticInnerClass { public static String staticName; public String name; public void foo(){} public static void staticFoo(){} } @Test public void testStaticInnerClassStaticField() throws Throwable { MethodHandle mhSet = lookup.findStaticSetter(StaticInnerClass.class, "staticName", String.class); String expected = "mama"; mhSet.invoke(expected); MethodHandle mhGet = lookup.findStaticGetter(StaticInnerClass.class, "staticName", String.class); Object obj = mhGet.invoke(); String value = (String)obj; assertSame(expected, value); } @Test public void testStaticInnerClassField() throws Throwable { StaticInnerClass sut = new StaticInnerClass(); Field f = StaticInnerClass.class.getDeclaredField("name"); MethodHandle mhSetUnreflect = lookup.unreflectSetter(f); String expectedUnreflect = "unreflect"; mhSetUnreflect.invoke(sut, expectedUnreflect); MethodHandle mhSet = lookup.findSetter(StaticInnerClass.class, "name", String.class); String expected = "mama"; mhSet.invoke(sut, expected); MethodHandle mhGet = lookup.findGetter(StaticInnerClass.class, "name", String.class); Object obj = mhGet.invoke(sut); String value = (String)obj; assertSame(expected, value); } @Test public void testStaticInnerClassConstructor() throws Throwable { StaticInnerClass sut = new StaticInnerClass(); MethodType type = MethodType.methodType(void.class); MethodHandle mh = lookup.findConstructor(StaticInnerClass.class, type); mh.invoke(); } @Test public void testStaticInnerClassMethod() throws Throwable { StaticInnerClass sut = new StaticInnerClass(); MethodType type = MethodType.methodType(void.class); MethodHandle mh = lookup.findVirtual(StaticInnerClass.class, "foo", type); mh.invoke(sut); } @Test public void testStaticInnerClassStaticMethod() throws Throwable { MethodType type = MethodType.methodType(void.class); MethodHandle mh = lookup.findStatic(StaticInnerClass.class, "staticFoo", type); mh.invoke(); } @SuppressWarnings("unused") private class InnerClass { public String name; public void foo(){} } @Test public void testInnerClassField() throws Throwable { InnerClass sut = new InnerClass(); MethodHandle mhSet = lookup.findSetter(InnerClass.class, "name", String.class); String expected = "mama"; mhSet.invoke(sut, expected); MethodHandle mhGet = lookup.findGetter(InnerClass.class, "name", String.class); Object obj = mhGet.invoke(sut); String value = (String)obj; assertSame(expected, value); } @Test public void testInnerClassConstructor() throws Throwable { MethodType type = MethodType.methodType(void.class, MethodHandlerCleanTest.class); //default constructor is private Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP"); field.setAccessible(true); MethodHandles.Lookup trustedLookup = (MethodHandles.Lookup) field .get(null); MethodHandle mh = trustedLookup.findConstructor(InnerClass.class, type); mh.invoke(this); } @Test public void testInnerClassMethod() throws Throwable { InnerClass sut = new InnerClass(); MethodType type = MethodType.methodType(void.class); MethodHandle mh = lookup.findVirtual(InnerClass.class, "foo", type); mh.invoke(sut); }

}


¿Cómo puedo obtener todos los métodos declarados a través de MethodHandles.lookup ()? ¿Cómo puedo obtener todos los campos declarados?

Piense en java.lang.invoke como una extensión (de ejecución rápida) a la reflexión (java.lang.reflect), es decir, las clases "invocar" dependen de las clases "reflexión".

  • Obtendrá referencia a todos los métodos / constructores / campos a través de la reflexión (java.lang.Class y java.lang.reflect):

    java.lang.Class<?> someClass = ...; // obtain a Class somehow // Returns all constructors/methods/fields declared in class, // whether public/protected/package/private, // but does NOT include definitions from any ancestors: java.lang.reflect.Constructor<?>[] declaredConstructors = someClass.getDeclaredConstructors(); java.lang.reflect.Method[] declaredMethods = someClass.getDeclaredMethods(); java.lang.reflect.Field[] declaredFields = someClass.getDeclaredFields(); // Returns all constructors/methods/fields declared as public members // in the class AND all ancestors: java.lang.reflect.Constructor<?>[] publicInheritedConstructors = someClass.getConstructors(); java.lang.reflect.Method[] publicInheritedMethods = someClass.getMethods(); java.lang.reflect.Field[] publicInheritedFields = someClass.getFields();

  • Los convierte a MethodHandles a través de java.lang.invoke.MethodHandles.Lookup:

    java.lang.invoke.MethodType mt; java.lang.invoke.MethodHandle mh; java.lang.invoke.MethodHandles.Lookup lookup = MethodHandles.lookup(); // process methods for (java.lang.reflect.Method method: declaredMethods) { mh = lookup.unreflect(method); // can call mh.invokeExact (requiring first parameter to be the class'' // object instance upon which the method will be invoked, followed by // the methodparameter types, with an exact match parameter and return // types) or // mh.invoke/invokeWithArguments (requiring first parameter to be the // class'' object instance upon which the method will be invoked, // followed by the method parameter types, with compatible conversions // performed on input/output types) } // process constructors for (java.lang.reflect.Constructor<?> constructor: declaredConstructors) { mh = lookup.unreflectConstructor(constructor); // can call mh.invokeExact or // mh.invoke/invokeWithArguments } // process field setters for (java.lang.reflect.Field field: declaredFields) { mh = lookup.unreflectSetter(field); // can call mh.invokeExact or // mh.invoke/invokeWithArguments } // process field getters for (java.lang.reflect.Field field: declaredFields) { mh = lookup.unreflectGetter(field); // can call mh.invokeExact or // mh.invoke/invokeWithArguments }

  • Puede determinar si la firma de los métodos / constructores / campos a través de java.lang.reflect:

    // If generics involved in method signature: Type[] paramTypes = method.getGenericParameterTypes(); Type returnType = method.getGenericReturnType(); // Note: if Class is non-static inner class, first parameter of // getGenericParameterTypes() is the enclosing class // If no generics involved in method signature: Class<?>[] paramTypes = declaredMethod.getParameterTypes(); Class<?> returnType = declaredMethod.getReturnType(); // Note: if Class is non-static inner class, first parameter of // getParameterTypes() is the enclosing class // Same method calls for declaredConstructor

  • Puede determinar si los métodos / constructores / campos son estáticos a través de java.lang.reflect:

    int modifiers = method.getModifiers(); // same method for constructor/field boolean isStatic = java.lang.Modifier.isStatic(modifiers);

¿Cuál es la diferencia entre MethodHandle.invoke (), MethodHandle.invokeExact () y MethodHandle.invokeWithArguments ()?

  • consulte http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html#invoke%28java.lang.Object...%29
  • Si el MethodHandle es para un método no estático, el primer parámetro proporcionado a estos métodos es una instancia de la Class que declara el método. El método se invoca en esta instancia de la clase (o en la propia Clase para métodos estáticos). Si la Class es una clase interna no estática, el segundo parámetro es una instancia de la clase envolvente / declarante. Los parámetros subsiguientes son los parámetros de la firma del método, en orden.
  • invokeExact no realiza conversión automática de tipos compatibles en los parámetros de entrada. Requiere que los valores de los parámetros (o las expresiones de los parámetros) coincidan exactamente con la firma del método, con cada parámetro proporcionado como un argumento separado O todos los argumentos proporcionados juntos como una matriz (firma: Object invokeExact(Object... args) ).
  • invoke requiere que los valores de los parámetros (o las expresiones de los parámetros) sean compatibles con el tipo compatible con la firma del método; se realizan conversiones automáticas de tipo, con cada parámetro proporcionado como un argumento separado O todos los argumentos se presentan juntos como una matriz (firma: Invocación del objeto (Objeto). .. args))
  • invokeWithArguments requiere que los valores de los parámetros (o las expresiones de los parámetros) sean compatibles con el tipo que concuerdan con la firma del método; se realizan conversiones automáticas de tipo, con cada parámetro incluido dentro de una Lista (firma: Object invokeWithArguments(List<?> arguments) )

Apreciaré el tutorial sobre el uso de MethodHandle API para Java devloper

No hay mucho por ahí, por desgracia. Podrías probar lo siguiente. Con suerte, he dado suficiente información arriba: ^)

http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html
http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandles.Lookup.html
http://www.java7developer.com/blog/?p=191
http://www.oraclejavamagazine-digital.com/javamagazine/20130102?pg=52&search_term=methodhandle&doc_id=-1#pg50
http://www.amazon.com/Well-Grounded-Java-Developer-techniques-programming/dp/1617290068


Como Balder dijo que ambas llamadas invoke e invoke invokeExact no aceptan argumentos pasados ​​como una matriz. (Sin embargo, sí aceptan argumentos de matriz).

int[] args = {1,1}; // handle for Math.addExact(int, int) Object o = handle.invokeExact(1,1); // OK Object o = handle.invoke(1,1); // OK Object o = handle.invokeExact(args); // ERROR Object o = handle.invoke(args); // ERROR

El error es siempre una excepción de tipo incorrecto:

java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(int, int)int to (Object[])int

Por lo que no desempaquetar la matriz. Pero pasar en una matriz donde se requiere trabajos:

// handle for Arrays.sort(int[]) handle.invokeExact(args); // OK handle.invoke(args); // OK

Como dijo Balder, la implementación del comportamiento deseado con invokeWithArguments() puede incurrir en una sobrecarga bastante considerable.

Para obtener el comportamiento deseado para desempaquetar listas de argumentos como se conoce en varargs, uno tiene que convertir el manejador en un esparcidor:

// handle for Math.addExact(int, int) handle = handle.asSpreader(int[].class, 2); handle.invokeExact(args); // OK handle.invoke(args); // OK

Por supuesto, la misma funcionalidad que para los argumentos explícitos que pasan cuentas nuevamente cuando la matriz se define como genérica:

Object[] args = new Object[]{1,1}; // handle for Math.addExact(int, int) handle = handle.asSpreader(int[].class, 2); handle.invokeExact(args); // ERROR handle.invoke(args); // OK

No he realizado ninguna comparación de rendimiento entre las llamadas. Para aquellos interesados, es bastante sencillo ampliar este punto de referencia: http://rick-hightower.blogspot.de/2013/10/java-invoke-dynamic-examples-java-7.html

Detalles:

Esencialmente invokeWithArguments hace cosas similares pero lo hace para cada llamada:

public Object invokeWithArguments(Object... arguments) throws Throwable { MethodType invocationType = MethodType.genericMethodType(arguments == null ? 0 : arguments.length); return invocationType.invokers().spreadInvoker(0).invokeExact(asType(invocationType), arguments); }

Por lo tanto, crear un esparcidor una vez y reutilizarlo producirá un rendimiento similar al de invoke e invoke invokeExact .