0

現在、私のリスナーは、内部メソッドを呼び出すためにスイッチ ツリーを必要とします。

public class Car{


   public void listener(String e){
      if(e.equals("Honk"))
        this.blowHorn();
   }

   @Honk
   private void blowHorn(){...}

}

実行時にリスナー メソッドを生成できるように、リフレクションとメソッド アノテーションを活用することは可能ですか? 入力がメソッド注釈と等しいかどうかに基づいて切り替えます。これは、オーバーヘッドを削減するため、単純なリフレクションを使用するよりも優れています。

4

3 に答える 3

2

*********************熟考して答える************************

最初に、新しいアノテーションを次のように宣言します。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface CarListener{
    public String carAction();
}

したがって、クラス Car では次のようになります。

public class Car {

    //Here you´ll be looking at all the methods  you have in the class Car (I´d advice to 
    // put them in another class, so as to keep it clean, I didn´t do it here in order to
    // explain it better. These methods have the corresponding annotation you created

    public void listener(String e) { 
        Method[] methods = Car.class.getMethods();
            for(Method method:methods) {

        //Now that you have all the methods all you need is to figure which one you want
        // you´ll do that according to the "e" string, which represents the car action (for 
        // example "Honk") I´d also advice you to rename that too.

            if(rightMethod(method, e))

                //Now that you have found it, then you invoke the method, "call it"
                // which is what you wre doing in the previos code with "this.blowHorn()"

                return invokeMethod(method);
        }
        //This will help you in case you did NOT find the correct method, it´s just help 
        // if you don´t put it in it won´t break your code
        // fun fact about RuntimExceptions: you don´t have to declare them, meaning
        // you dont have to add them as "throws" or catch

        throw new RuntimeException("No listener found for car Action"+e);
    }

    private boolean rightMethod(Method method, String expression) {

    //First if asks if the method found has an annoation present, and if it does
    // then it asks if it corresponds to the annoation you created

        if (method.isAnnotationPresent(NewAnnotationInterfaceName.class))

    //if the method in fact has the annotation created all you are doing is asking what
    // carAction is associated to that method, you do that with the .carAction()

            return method.getAnnotation(NewAnnotationInterfaceName.class).carAction().equals(expression);
        return false;
    }


    //Now all you have to do is invoke it :) This just follows how to invoke a method
    // I won´t explain it

    private void  invokeMethod(Method method) {
        try {
            return method.invoke(Car.class.newInstance(), new Object[]{});
        } catch (InstantiationException | IllegalAccessException
                 | InvocationTargetException | IllegalArgumentException ex) {
            Logger.getLogger(Car.class.getName()).log(Level.SEVERE, null, ex);
        }
        throw new RuntimeException("Could not invoke method");
    }


    @CarListener(carAction= "Honk")
    public void blowHorn() {  
        ...
    }

     @CarListener(carAction= "SomethingElse")
    public void someOtherAction() {  
        ...
    }

}

それが役立つことを願っています!

******************** ハッシュマップとコマンド設計で回答***********************

public abstract class CarAction {

    public abstract void execute(Car car){};

}

public class HonkAction extends CarAction{

    @Override
    public void execute(Car car) {
           car.blowHorn();
    }    

}

public class Car {

    private HashMap<String, CarAction> carActions;

    public Car() {
        ...
        initializeCarActions();
    }

    public void initializeCarActions() {
        this.carActions = new HashMap<>();
        this.carActions.put("Honk", new HonkAction());
        ...
    }

    public void listener(String e) {
        CarAction action = this.carActions.get(e);
        if(action!=null) action.execute(this);
    }

}

この方法を使用する場合、誰かに HashMap を注入してもらい、Car が CarActions (抽象クラスのみ) に依存する必要がないようにすることをお勧めします。また、これは、実行するためにすべての carActions が「Car」のみを必要とする場合に機能します。

幸運を!

于 2013-04-02T20:48:14.783 に答える
0

アノテーションでこれを本当にやりたい場合は、次のようなものでそれを行うことができます:

public void listener(String e) {
    for (Method m : this.getClass().getDeclaredMethods()) {
        for (Annotation a : m.getDeclaredAnnotations()) {
            if (a.getClass().getSimpleName().equals(e)) {
                m.setAccessible(true); // Need to do this so we can run private methods
                m.invoke(this);
            }
        }
    }
}

これにより、指定された名前と一致する名前を持つ注釈を使用して各メソッドが呼び出されます。この一般的な設計に固執して、2 つのコアの改善を提案します。

  • 上記を実行してアノテーション <-> メソッド マップを事前に構築しますが、それを呼び出す代わりにアノテーション名 -> メソッド ハッシュマップに格納します。

  • Strings(listener(Class<? extends Annotation> annotation)の代わりにlistener(String e)) の代わりにアノテーションのインスタンスを取得することで、名前を取得して文字列を比較するのではなく、equals を使用して入力をメソッドのアノテーションと比較することができます。

ただし、これは優れた設計ではありません。より一般的には、これらを一連の単純な if ステートメントとしてハードコア化するか (オプションの数が少ない場合)、コマンド パターンのようなものを使用するオプションがかなりある場合 (各呼び出し可能なメソッドをオブジェクトにカプセル化します) )、各コマンドを作成時に名前で登録し、これらをマップに保存するだけで、そこから検索して関連するコマンドを見つけることができます。

于 2013-04-02T20:44:48.967 に答える