2016-07-05 20 views
1

コールバックでメソッドクラス変数を渡すにはどうすればよいですか?Javaはコールバックで関数として文字列変数を渡します。

は、私は次のコードを持っている:

PostsController postController = new PostsController(); 
router.get("/").handler(postController::index); 

をしかし、私は動的クラスとその関数を渡すことができますどのように私のコントローラは、ダイナミックになります。私は私が見ることができるコード

Class controller = Class.forName("some.package"+controllerName); //PostsController 
Method[] methods = controller.getMethods(); 
//some loop 
Method m = methods[i]; // This is the index method of my controller 
router.get(somePath).handler(m); // How can I pass my method m as callback? 

PostsControllerの

public class PostsController { 

    @Route(method="get", path = "/") 
    public void index(RoutingContext routingContext){ 
     routingContext.response() 
       .putHeader("content-type", "application/json; charset=utf-8") 
       .end(Json.encodePrettily("{}")); 
    } 

} 
+4

すべてのコントローラにこの 'index'メソッドがあることを保証する必要はありませんか?あなたのコントローラがインデックスメソッドを必要とするいくつかのインタフェースを拡張するようにして、それを解決してください。 – ifly6

+0

インデックス関数はそれらになりますが、カスタム関数もいくつかあります。なぜそれを動的にしたいのですか? –

+2

はい、カスタム関数はすべて、そのファイルがインターフェイスを実装している限り、ファイル内で宣言できます。ただし、JVMは、ファイルにそのメソッドがあることを保証できるかどうかはわかりません。すべてのコントローラーに共通のインターフェースを実装させることで、これを保証することができます。 – ifly6

答えて

2

最もクリーンな解決策は、共通機能を定義するinterfaceを使用することです。もちろん

public interface Controller { 
    void index(RoutingContext routingContext); 
} 
public class PostsController implements Controller { 
    @Route(method="get", path = "/") 
    public void index(RoutingContext routingContext){ 
     routingContext.response() 
       .putHeader("content-type", "application/json; charset=utf-8") 
       .end(Json.encodePrettily("{}")); 
    } 
} 

Class<? extends Controller> controllerType = 
    Class.forName("some.package"+controllers.getString(i)) 
     .asSubclass(Controller.class); 
Controller controller=controllerType.newInstance(); 
Handler<RoutingContext> h=controller::index; 

、これは注釈ベースの処理と相互作用しません。ダイナミックな解決策は、これはあなたが指定されていませんでしたあなたのHandlerインタフェースに関する特定の仮定の下で動作します

Class<?> controllerType = Class.forName("some.package"+controllers.getString(i)); 
MethodHandles.Lookup lookup=MethodHandles.lookup(); 
Method[] methods = controllerType.getMethods(); 
Object instance = null; 
for (Method m : methods) { 
    Route annotation = m.getAnnotation(Route.class); 
    if (annotation != null) { 
     if(instance == null) instance = controllerType.newInstance(); 
     Handler<RoutingContext> handler 
      =createHandler(lookup, instance, m, RoutingContext.class); 
     router.get(annotation.path()).handler(handler); 
    } 
} 

static <T> Handler<T> createHandler(MethodHandles.Lookup lookup, 
     Object instance, Method m, Class<T> arg) throws Throwable { 

    MethodHandle mh=lookup.unreflect(m); 
    MethodType t=mh.type(); 
    Class<?> receiver=t.parameterType(0); 
    if(t.parameterCount()!=2 
    || !receiver.isAssignableFrom(instance.getClass()) 
    || !t.parameterType(1).isAssignableFrom(arg)) 
     throw new IllegalArgumentException(m+" not suitable for Handler<"+arg.getName()+'>'); 
    t=t.dropParameterTypes(0, 1); 
    return (Handler)LambdaMetafactory.metafactory(lookup, "accept", 
     MethodType.methodType(Handler.class, receiver), 
     t.changeParameterType(0, Object.class), mh, t) 
     .getTarget().invoke(instance); 
} 

ようになります。 interface Handler<T> extends Consumer<T> {}のように定義されていれば、それはそのままで動作します。それ以外の場合は、機能インタフェースの関数メソッドの実際の名前に"accept"を適合させる必要があります。また、型パラメータにバウンドがある場合は、t.changeParameterType(0, Object.class)行を実際のバウンド(型消去後のHandlerのパラメータ型)を使用するように変更する必要があります。

一般的に、これはすぐにエラーフィードバックがないので、非常に注意が必要です。最悪の場合、エラーはコールバックが実際に呼び出されたときにのみ検出されます(特定の引数で)。

原則として

、あなたもちょうどすなわち

Handler<RoutingContext> handler=rc -> { try { 
    m.invoke(capturedInstance, rc); 
} catch(ReflectiveOperationException ex) { ex.printStackTrace(); }}; 

、メソッドの反射呼び出しをカプセル化するラムダ式を作成することもできますが、これはあなたが非常に遅く、エラーを検出されるという事実は変わりません...

+0

ありがとう、私はこれを試しましたが、このエラーを投げます: 'java.lang.AbstractMethodError:io.vertx.lib.AppVertical $$ Lambda $ 28/647430455.handle(Ljava/lang/Object;)V' –

+1

このように、メソッド名を変更する必要があります: '' accept ''' 'handle" '。 – Holger

+0

ありがとう、これはうまくいきました:) インターフェイスはきれいで簡単ですが、コントローラで動的メソッドを使用すると、さらに洗練された方法があります。 –

0

まず最初に、次の試してみました は、あなたがあなたのクラスのインスタンスを作成する必要があるということです。

Class controller = Class.forName("some.package"+controllers.getString(i)); 
Object myInstance = controller.newInstance(); 

次に、あなたの方法は、それから取得し、それを呼び出す:

Method myMethod = ... 
myMethod.invoke(myInstance, null); // or with some params 

あなただけの「インデックス」を呼び出す必要がある場合、また、あなたのコントローラが共通のインターフェースを実装行い、その後、できます。

MyInterface myInterface = (MyInterface) myInstance; 
myInterface.index(); 

希望しました。

+0

私はこの 'router.get(annotation.path())。handler((Handler )m.invoke(controller.newInstance()、null)));)を試しました。' java.lang .IllegalArgumentException:引数の数が正しくありません.' –

+0

明らかに、メソッドのインデックスにはパラメータがあるので、それを指定する必要があります。 –

+0

しかし、呼び出しは実際に関数を実行します、私はコールバックとして渡したいと思います。ハンドラはパラメータをコールバック関数 –

関連する問題