关于MethodHandle API的一些基本问题

如何通过MethodHandles.lookup()获取所有声明的方法? 我如何获得所有声明的字段?

MethodHandle.invoke()MethodHandle.invokeExact()MethodHandle.invokeWithArguments()区别是什么?

另外,我将非常感谢有关使用Methodhandle API for Java devloper的教程 。 我强调,我正在编写静态类型语言普通旧Java,我不是JVM开发人员,特别是我对整个字节码废话(invokedynamic)并不感兴趣。 我想弄清楚如何使用这个新API而不是Java Core API。

EDITED-2:

@Glen Best下面提供了一些我只想提供的参考资料http://www.oraclejavamagazine-digital.com/javamagazine/20130102?pg=52&search_term=methodhandle&doc_id=-1#pg50这正是我想要的。 我发现实际上有一些新的词汇表。例如, 目标实际上是指MethodHandle(而不是对象进行调度),而调用站点实际上是“调用”“函数指针”又称MethodHandle的代码。 此外,必须了解MethodHandle API 不是替代 Core Reflection API而不是补充它。 例如,您无法使用MethodHandle“发现”所有方法,而您需要Core Reflection API。 但是当你“找到”你想要的方法时,你可以切换到MethodHandle,例如,绑定一些参数,或者将它的签名“改变”(改编)给varargs。

编辑:

我仍然想找出答案。 我写了一些我想与大家分享的测试。

 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); } 

}

如何通过MethodHandles.lookup()获取所有声明的方法? 我如何获得所有声明的字段?

将java.lang.invoke视为reflection(快速执行)的reflection扩展(java.lang.reflect) – 即“调用”类依赖于“reflection”类。

  • 您可以通过reflection(java.lang.Class和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(); 
  • 您可以通过java.lang.invoke.MethodHandles.Lookup将它们转换为MethodHandles:

     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 } 
  • 您可以通过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 
  • 您可以通过java.lang.reflect确定方法/构造函数/字段是否是静态的:

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

MethodHandle.invoke(),MethodHandle.invokeExact()和MethodHandle.invokeWithArguments()之间有什么区别?

  • 见http://docs.oracle.com/javase/7/docs/api/java/lang/invoke/MethodHandle.html#invoke%28java.lang.Object…%29
  • 如果MethodHandle用于非静态方法,则提供给这些方法的第一个参数是声明该方法的Class的实例。 在该类的实例上调用该方法(或者在静态方法的类本身上调用)。 如果Class是非静态内部类,则第二个参数是封闭/声明类的实例。 后续参数是方法签名参数,按顺序。
  • invokeExact不对输入参数执行自动兼容类型转换。 它要求参数值(或参数表达式)与方法签名完全匹配,每个参数作为单独的参数提供,或者所有参数作为数组一起提供(签名: Object invokeExact(Object... args) )。
  • invoke要求参数值(或参数表达式)与方法签名类型兼容 – 执行自动类型转换,每个参数作为单独的参数提供或者所有参数作为数组一起提供(签名:对象调用(对象)。 .. args))
  • invokeWithArguments要求参数值(或参数表达式)与方法签名类型兼容 – 执行自动类型转换,每个参数在List中提供(签名: Object invokeWithArguments(List arguments)

关于为Java devloper使用MethodHandle API的教程,我将不胜感激

不幸的是,那里并不多。 您可以尝试以下方法。 希望我已经给出了足够的信息:^)

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

MethodHandle.invoke(),MethodHandle.invokeExact()和MethodHandle.invokeWithArguments()之间的区别是什么?

由于我也在努力解决这个问题,所以我决定重新审视这个问题并编写一个示例来准确显示这些方法之间的语义差异。

主要区别是:

  1. invokeExact 接受精确的参数和返回类型,并且不接受作为数组的参数。 调用eg方法签名(Integer,Integer)Integer不允许(Integer,Integer)Integer使用int参数,但是也不允许使用Object参数调用它,即使该对象实际上是Integer类型 – 参数的compiletime类型必须是class整数,而不是运行时实例:

     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自动转换参数类型和返回类型,并在原始类型和相应的包装类之间进行转换。 但它也不接受作为数组的参数。 例如,方法签名(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删除所有这些限制,并且与Method#invoke非常相似 – 您还可以提供一个数组(或java.util.List )作为参数(但这种灵活性会带来巨大的性能损失)。 例如,方法签名(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! 

这是一个完整的例子:

 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)); } } 

正如Balder所说,调用invokeinvokeExact都不接受作为数组传入的参数。 (但是,它们接受数组参数。)

 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 

该错误始终是错误的类型exception:

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

所以它不解包数组。 但是传入一个需要它的数组:

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

正如Balder所说,使用invokeWithArguments()实现所需的行为可能会产生相当大的开销。

为了从varargs中获得解压缩参数列表所需的行为,必须将句柄转换为spreader:

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

当然,当数组被定义为通用时,与显式参数传递帐户相同的function:

  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 

我没有在电话之间进行任何性能比较。 对于那些感兴趣的人来说,扩展这个基准是非常简单的: http : //rick-hightower.blogspot.de/2013/10/java-invoke-dynamic-examples-java-7.html

细节:

基本上invokeWithArguments做类似的事情,但每次调用都这样做:

  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); } 

因此,创建一个spreader invokeExact它,很可能会产生与invokeinvokeExact类似的性能。