3

私はこの問題に一日を費やしました。私の問題は、クラス型がプログラムの実行時に動的にロードされるインスタンスで MethodHandle.invokeExact 呼び出しを行う方法です。問題をより明確にするために、サンプルコードを以下に示します。

Class<?> expClass = new MyClassLoader().load(....)

//expClass is AddSample.class which is subclass of BaseTemplate         
 BaseTemplate obj = expClass.getConstructor(...)
                .newInstance(...);
MethodHandle myMH = MethodHandles.lookup().findVirtual(expClass, methodName,..);
System.out.println("Object type "+obj.getClass()); //Print AddSample

// If obj is declared as "AddSample obj", the runtime would be OK.
assertEquals((int)myMH.invokeExact(obj,"addintaasdsa" , 10 , 20.0f), 12);

このサンプルでは、​​expClass が動的にロードされ、そのクラス タイプはAddSampleです。次の行の obj インスタンスは BaseTemplate であると宣言されており、その実際の型はAddSample. クラス AddSample は BaseTemplate のサブクラスです。次に、メソッドハンドル myMh を add 関数に作成しAddSampleますが、receiverType が一致しないため、myMH の呼び出しは失敗します。

myMH.invokeExact実行時エラーを発生させます

java.lang.invoke.WrongMethodTypeException: expected (AddSample,String,int,float)int but found (Object,String,int,float)int

this のレシーバーはmyMHexpClass (AddSample) にあると宣言されてobjいますが、obj のクラスは AddSample ですが、現在提供されているレシーバーは BaseTemaplte と宣言されているためです。InvokeExact では、パラメーターが正確に一致する必要があります。


私の問題は次のように単純化される可能性があります:インスタンスをその基本型から動的にロードされる子型にキャストする方法は?

BaseTemplate obj = ...
Class<?> newType = Class('AddSample') //dynamic loaded...

obj の宣言された型を、動的にロードされる AddSample に変更します..?

UPDATE:

Class<T> expClass = (Class<T>) new MyClassLoader().run(className, methodName, b);
BaseTemplate obj = ..
Class<T> newType = (Class<T>) obj.getClass().getClassLoader().loadClass("AddSample");
T tObj = newType.cast(obj);
assertEquals((int)myMH.invokeExact(tObj,"addintaasdsa" , 10 , 20.0f), 12);

キャストを使用しても、以前の結果と同じ問題を解決するのには役立ちません。その理由は、指定されたパラメーターが myMH 宣言と完全に一致していないためです。生成されたバイトコードを確認すると、より明確になります。

L23  # For cast 
    LINENUMBER 126 L23
    ALOAD 10: newType
    ALOAD 8: obj
    INVOKEVIRTUAL Class.cast (Object) : Object  #tObj is Object and its real type is AddSample here
    ASTORE 11
   L24
    LINENUMBER 128 L24
    ALOAD 9: myMH           # Push myMH to stack
    ALOAD 11: tObj          # Push tObj to Stack. tObj is declared Object type and its real type is AddSample. 
    LDC "addintaasdsa"      #Push String to Stack
    BIPUSH 10               #Push int to Stacl
    LDC 20.0                #Push float to Stack 
    INVOKEVIRTUAL MethodHandle.invokeExact (Object, String, int, float) : int

myMH は を指して (AddSample,String,int,float)intいますが、パラメーター: (Object, String, int, float)を指定すると、前に示した実行時エラーが発生します。

ありがとう

4

2 に答える 2

2

invokeExact引数のコンパイル時の型がMethodHandleのパラメーターの型と一致しない場合は使用できません。castを呼び出すようなジェネリック構造をいじっても役に立ちませんClass<T>。動的な型はコンパイラにはまだ不明です。

または、言い換えれば、型の消去により、型tObjはまだObjectバイトコードレベルにあります。そして、これが の「呼び出されるタイプ」ですMethodHandle

最も簡単な解決策は、invokeではなくを使用することですinvokeExact

を使用したい場合にできる唯一のことはinvokeExact、 を最終的に呼び出すタイプに変換するMethodHandleことです。つまり、最初のパラメータのタイプを に変更しますObject

myMH=myMH.asType(myMH.type().changeParameterType(0, Object.class));
// now it doesn’t matter that obj has compile-time type Object
assertEquals((int)myMH.invokeExact(obj, "addintaasdsa", 10, 20.0f), 12);
于 2015-02-19T19:47:13.247 に答える
0

意味をなすには、メソッドを入力する必要があります (そうしないと、キャストは無意味です)。

public <T> void doSomething() {

BaseTemplate obj = ...
Class<T> newType = Class('AddSample');
T t = newType.cast(obj);

メソッドの型付けがなければ、動的クラスをキャスト先の型に関連付けることはできません。

于 2015-02-14T05:00:19.073 に答える