首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >关于MethodHandle API的几个基本问题

关于MethodHandle API的几个基本问题
EN

Stack Overflow用户
提问于 2013-04-15 00:12:13
回答 3查看 12.4K关注 0票数 24

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

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

此外,我将非常感谢关于如何使用MethodHandle API for Java 的教程。我要强调的是,我是在静态类型化语言普通的旧Java上编程,我不是JVM开发人员,特别是我对整个字节代码垃圾(invokedynamic)不感兴趣。我想弄清楚如何使用这个新API而不是。

编辑-2:

@Glen提供了一些参考资料,我只想提供一个id=-1#pg50,这正是我想要的。我发现实际上有一些新的词汇..。例如,目标实际上是指MethodHandle (而不是发出分派的对象),而调用站点实际上是“调用”“函数指针”(又称MethodHandle )的代码。同样重要的是要理解MethodHandle API是,而不是替代核心反射API,而不是包含它。例如,您不能使用MethodHandle“发现”所有需要核心反射API的方法。但是当您“找到”您想要的方法时,您可以切换到MethodHandle,例如,绑定它的一些参数或“更改”(适应),例如它的签名到varargs。

编辑:

我还在努力找出答案。我写了一些测试,我想和大家分享。

代码语言:javascript
复制
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);
    }

}

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-04-24 05:35:18

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

将java.lang.invoke看作是反射(java.lang.reflect)的(快速执行)扩展--即“调用”类依赖于“反射”类。

  • 通过反射(java.lang.Class和java.lang.reflect)获得对所有方法/构造函数/字段的引用: java.lang.Class[] declaredConstructors = someClass.getDeclaredConstructors();java.lang.reflect.Method[] declaredMethods = someClass.getDeclaredMethods();java.lang.reflect.Field[] declaredFields = someClass.getDeclaredFields();//返回类和所有祖先中声明为公共成员//的所有构造函数/方法/字段: java.lang.reflect.Constructor[] publicInheritedConstructors = someClass.getConstructors();java.lang.reflect.Method[] publicInheritedMethods = someClass.getMethods();java.lang.reflect.Field[] publicInheritedFields = someClass.getFields();
  • 通过MethodHandles将它们转换为java.lang.invoke.MethodHandles.Lookup: java.lang.invoke.MethodType mt;java.lang.invoke.MethodHandle mh;java.lang.invoke.MethodHandles.Lookup查找= MethodHandles.lookup();//处理方法(java.lang.reflect.Method方法: declaredMethods) { mh =lookup.unreflect(方法);//可以调用mh.invokeExact (要求第一个参数是调用方法的类‘// object实例,然后是//方法参数类型,具有精确的匹配参数和返回//类型)或// mh.Invoke/ invoked /invokeWithArguments(要求第一个参数是调用方法的// class’对象实例,//后面跟着方法参数类型,(java.lang.reflect.Constructor构造函数: declaredConstructors) { mh =lookup.unreflectConstructor(构造函数)的} //过程构造函数;//可以调用mh.invokeExact或// mh.invoke/invokeWithArguments } // process字段设置器,用于(java.lang.reflect.Field字段: declaredFields) { mh =lookup.unreflectSetter(字段);//可以调用mh.invokeExact或// mh.invoke/invokeWithArguments } // java.lang.reflect.Field字段getter for (java.lang.reflect.Field字段: declaredFields) { mh =lookup.unreflectGetter(字段);//可以调用mh.invokeExact或// mh.invoke/invokeWithArguments }
  • 您可以通过java.lang.reflect确定方法/构造函数/字段的签名: //如果方法签名涉及到泛型: Type[] paramTypes = method.getGenericParameterTypes();类型returnType = method.getGenericReturnType();//注意:如果类是非静态内部类,则// getGenericParameterTypes()的第一个参数是封装类//如果方法签名中不涉及泛型: class returnType = declaredMethod.getReturnType();//注意:如果class是非静态内部类,那么// getParameterTypes()的第一个参数是对declaredConstructor的封闭类//相同方法调用。
  • 您可以通过java.lang.reflect确定方法/构造函数/字段是否是静态的: int修饰符= method.getModifiers();//构造函数/字段布尔isStatic =java.lang.Modifier.isStatic(修饰符)的相同方法;

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 .(Args)
  • invokeWithArguments要求参数值(或参数表达式)与方法签名匹配,执行自动类型转换,在列表中提供每个参数(签名:Object invokeWithArguments(List<?> arguments))。

我将非常感谢关于如何使用的教程。

不幸的是,外面没有太多东西。你可以试试以下的方法。希望我在上面给出了足够的信息:^)

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

id=-1#pg50

http://www.amazon.com/Well-Grounded-Java-Developer-techniques-programming/dp/1617290068

票数 26
EN

Stack Overflow用户

发布于 2015-05-08 06:51:02

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

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

主要的区别是:

  1. invokeExact 只接受确切的参数和返回类型,而而不是接受参数作为数组。不允许使用int参数调用方法签名int,但也不允许使用Object参数调用方法签名,即使对象实际上是Integer类型--参数的编译时类型必须是类整数,而不是运行时实例: 对象arg = 1;Object[] args = {1,1};整数I=(整数)handle.invokeExact(1,1);// OK对象o= handle.invokeExact(arg,arg);// ERROR handle.invokeExact(args);//错误

  1. invoke自动转换参数类型和返回类型,并在基本类型和对应的包装类之间进行转换。但是它也是,而不是,它接受参数作为数组。例如,对于方法签名,(Integer,Integer)Integer 对象arg = 1;Object[] args = {1,1};整数i=(整数)handle.invoke(1,1);// OK对象o= handle.invoke(arg,arg);// OK!O= handle.invoke(args);//错误

  1. invokeWithArguments消除了所有这些限制,并与Method#invoke非常相似-您也可以提供一个数组(或java.util.List)作为参数(但是这种灵活性带来了巨大的性能损失)。例如,对于方法签名,(Integer,Integer)Integer 对象arg = 1;Object[] args = {1,1};整数i=(整数)handle.invokeWithArguments(1,1);// OK对象o= handle.invokeWithArguments(arg,arg);// OK o= handle.invokeWithArguments(args);// OK!

这里是一个完整的例子:

代码语言:javascript
复制
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));


    }
}
票数 12
EN

Stack Overflow用户

发布于 2016-01-13 09:06:39

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

代码语言:javascript
复制
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

错误总是错误类型的异常:

代码语言:javascript
复制
java.lang.invoke.WrongMethodTypeException: cannot convert MethodHandle(int, int)int to (Object[])int

所以它不解压缩数组。但是,在需要它的地方传入一个数组是有效的:

代码语言:javascript
复制
// handle for Arrays.sort(int[]) 
handle.invokeExact(args); // OK
handle.invoke(args); // OK

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

为了获得从varargs已知的解压缩参数列表所需的行为,必须将句柄转换为一个扩展器:

代码语言:javascript
复制
 // handle for Math.addExact(int, int)
 handle = handle.asSpreader(int[].class, 2);
 handle.invokeExact(args); // OK
 handle.invoke(args); // OK

当然,当数组定义为泛型时,与显式参数再次传递帐户的功能相同:

代码语言:javascript
复制
 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做类似的事情,但对每个调用都这样做:

代码语言:javascript
复制
 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);
}

因此,创建一次扩展器并重用它,很可能会产生与invokeinvokeExact相似的性能。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/16005824

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档