23

宣言されたすべてのメソッドを取得するにはどうすればよいMethodHandles.lookup()ですか? 宣言されたすべてのフィールドを取得するにはどうすればよいですか?

との違いMethodHandle.invoke()は何ですかMethodHandle.invokeExact()MethodHandle.invokeWithArguments()

また、 Java 開発者向けの MethodHandle API の使用に関するチュートリアルを提供していただければ幸いです。私は静的に型付けされた言語のプレーンな古いJavaでプログラミングしていて、JVM開発者ではありません。特に、バイトコードのがらくた全体(invokedynamic)には興味がありません。Java Core API の代わりにこの新しい API を使用する方法を知りたいです。

編集済み-2:

以下の@Glen Bestは、私が1つだけ提供したいいくつかの参照を提供しましたhttp://www.oraclejavamagazine-digital.com/javamagazine/20130102?pg=52&search_term=methodhandle&doc_id=-1#pg50これはまさに私が探していたものです。実際にはいくつかの新しい語彙があることがわかりました..たとえば、ターゲットとは実際には MethodHandle を意味し (ディスパッチを行うオブジェクトではありません)、呼び出しサイトは実際には MethodHandle として「関数ポインター」を「呼び出す」コードです。また、MethodHandle API は補足ではなくCore Reflection APIの代替ではないことを理解することが不可欠です。それ。たとえば、Core Reflection API が必要な MethodHandle を使用してすべてのメソッドを「検出」することはできません。ただし、目的のメソッドを「見つけた」場合は、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);
    }

}

4

3 に答える 3

26

MethodHandles.lookup() を介して宣言されたすべてのメソッドを取得するにはどうすればよいですか? 宣言されたすべてのフィールドを取得するにはどうすればよいですか?

java.lang.invoke をリフレクション (java.lang.reflect) の (高速実行) 拡張と考えてください。つまり、「invoke」クラスは「リフレクション」クラスに依存しています。

  • リフレクション (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が非静的内部クラスの場合、2 番目のパラメーターは、囲んでいる/宣言しているクラスのインスタンスです。後続のパラメーターは、順番にメソッド シグネチャ パラメーターです。
  • invokeExact入力パラメーターで互換性のある自動型変換を行いません。パラメーター値 (またはパラメーター式) がメソッド シグネチャと正確に型が一致している必要があります。各パラメーターは個別の引数として提供されるか、すべての引数が配列 (署名: Object invokeExact(Object... args)) として一緒に提供されます。
  • invokeパラメーター値 (またはパラメーター式) は、メソッド シグネチャと一致する型の互換性がある必要があります。自動型変換が実行され、各パラメーターが個別の引数として提供されるか、すべての引数が配列として一緒に提供されます (署名: Object invoke(Object.. .args))
  • invokeWithArgumentsパラメーター値 (またはパラメーター式) は、メソッド シグネチャと一致する型の互換性がある必要があります - 自動型変換が実行され、各パラメーターはリスト内で提供されます (シグネチャ: Object invokeWithArguments(List<?> arguments))

Java開発者向けの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

于 2013-04-24T05:35:18.937 に答える
12

MethodHandle.invoke()、MethodHandle.invokeExact()、および MethodHandle.invokeWithArguments() の違いは何ですか

私もこれに苦労したので、この質問を再検討し、これらのメソッド間の意味的な違いが何であるかを正確に示す例を書くことにしました.

主な違いは次のとおりです。

  1. invokeExact 正確な引数と戻り値の型のみを受け入れ、引数を配列として受け入れません。(Integer,Integer)Integerたとえば、引数を使用してメソッド シグネチャを呼び出すことは許可されていませんが、オブジェクトが実際には Integer 型であっても、引数を使用intして呼び出すこともできません。引数のコンパイル時の型は、ランタイム インスタンスではなく、クラス Integer である必要があります。Object

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


    }
}
于 2015-05-08T06:51:02.387 に答える
2

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

エラーは常に間違った型の例外です:

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 から知られているように、引数リストをアンパックするための望ましい動作を取得するには、ハンドルをスプレッダーに変換する必要があります。

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

もちろん、明示的な引数の受け渡しと同じ機能は、配列がジェネリックとして定義されている場合にも当てはまります。

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

そのため、スプレッダーを一度作成して再利用すると、invokeおよび と同様のパフォーマンスが得られる可能性が高くなりinvokeExactます。

于 2016-01-13T09:06:39.910 に答える