关于MethodHandle API的一些基本问题

ale*_*ail 21 java reflection java-7

如何通过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是不是更换为核心映像API,而不是suplement它.例如,您无法使用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);
    }
Run Code Online (Sandbox Code Playgroud)

}

Gle*_*est 24

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

将java.lang.invoke视为反射(快速执行)的反射扩展(java.lang.reflect) - 即"调用"类依赖于"反射"类.

  • 您可以通过反射(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();
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以通过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 
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以通过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
    
    Run Code Online (Sandbox Code Playgroud)
  • 您可以通过java.lang.reflect确定方法/构造函数/字段是否是静态的:

    int modifiers = method.getModifiers();  // same method for constructor/field
    boolean isStatic = java.lang.Modifier.isStatic(modifiers);
    
    Run Code Online (Sandbox Code Playgroud)

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 要求参数值(或参数表达式)与方法签名类型兼容 - 执行自动类型转换,每个参数作为单独的参数提供或者所有参数作为数组一起提供(签名:Object invoke(Object .. .args))
  • invokeWithArguments需要的参数值(或参数表达式)是类型兼容匹配方法签名-自动类型转换被执行,以列表内提供的每个参数(签名: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

  • `OR 作为数组一起提供的所有参数` - 我不认为这是正确的。`invoke` 和 `invokeExact` 似乎都不接受包含参数的数组(可变参数在这方面非常具有误导性)。只有 `invokeWithArguments` 将接受作为数组提供的参数,然后其行为与 `Method#invoke(Object, Obect...)` 非常相似。 (2认同)
  • 另外严格来说`“调用”类依赖于“反射”类`并不完全正确。`invokeExact` 和 `invoke` 方法是使用内部 `@PolymorphicSignature` 批注的 *native* 方法,`它区分(对 Java 编译器而言)签名多态的那些方法(PolymorphicSignature javadoc)。另请参阅:[签名多态性](https://docs.oracle.com/javase/8/docs/api/java/lang/invoke/MethodHandle.html#sigpoly)。MethodHandle 类不依赖于 java.lang.reflect 包中的任何类。 (2认同)

Bal*_*der 7

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

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

主要区别是:

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

    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
    
    Run Code Online (Sandbox Code Playgroud)

  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
    
    Run Code Online (Sandbox Code Playgroud)

  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!
    
    Run Code Online (Sandbox Code Playgroud)

这是一个完整的例子:

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


    }
}
Run Code Online (Sandbox Code Playgroud)