2013-08-27 85 views
1

好的,這很容易解釋。我會盡力。使用Java反射處理事件

受Bukkit事件系統的啓發,您只需使用@EventHandler就可以使事件處理程序變爲空白。

例子:

@EventHandler 
public void aRandomName(PlayerMoveEvent ev) { 

} 

正如你所看到的,方法的名稱並不重要。傳遞的事件由事件參數類型確定。

所有事件都會擴展Event類。 我已經編寫了一些我認爲可行的代碼,除了一件事。

public List<Object> eventContainers; 

public void fireEvent(Event e) { 

    Method[] methods; 

    for (Object o : eventContainers) { 
     Object[] classes = o.getClass().getClasses(); 

     for (Object clss : classes) { 
      methods = clss.getClass().getMethods(); 
      for (Method m : methods) { 
       if (m.getAnnotation(EventHandler.class) != null) { 
        try { 
         Class[] requiredTypes = m.getParameterTypes(); 
         for(Class cl : requiredTypes) { 
          if(e.equals(cl)) { 
           m.invoke(clss, e); 
          } 
         } 

        } catch (IllegalAccessException ex) { 
        } catch (IllegalArgumentException ex) { 
        } catch (InvocationTargetException ex) { 
        } 
       } 
      } 
     } 
    } 
} 

什麼我的代碼呢: 循環遍歷eventContainers所有類,尋找那些具有@EventHandler註釋和指定的事件發送到該方法的方法。但是,我想查看fireEvent(Event e)中給定事件是什麼類型的事件,然後查看需要此類事件參數的方法。我會怎麼做?我想,

Class[] requiredTypes = m.getParameterTypes(); 
for(Class cl : requiredTypes) { 
    if(e.equals(cl)) { 
    m.invoke(clss, e); 
    } 
} 

將無法​​正常工作。

最終我希望能夠將事件傳遞給插件。就像這樣:

EventManager.fireEvent(new PlayerMoveEvent(player)); 

將被髮送到所有的插件,並有

@EventHandler 
public void aVoid(PlayerMoveEvent e) { 
//stuff 
} 

如果您有任何疑問插件,我會盡量解釋更好。在此先感謝您的幫助!

回答

0

你的代碼使用e.equals(cl),這是與Class(類的Event一個實例)的實例比較Event一個實例 - 這將永遠不會返回true。你想要做的,而不是什麼是:

if(e.getClass().equals(cl)) { 
    m.invoke(clss, e); 
} 

或者,如果你想與@EventHandler批註處理類的子類的方法,他們的方法簽名定義(即像handle(Event e)的方法將與PlayerMoveEvents以及所有其他事件被稱爲),然後你想:

if(cl.isAssignableFrom(e.getClass())) { 
    m.invoke(clss, e); 
} 

更多信息,請參見Class Javadoc here

請注意,我認爲你的代碼還有其他一些問題。例如,應使用包含使用@EventHandler進行註釋的方法的類實例調用Method.invoke。它是從你的代碼有點不清楚,但我相信,因此這應該是:

m.invoke(o, e); 

此外,通過調用o.getClass().getClasses(),你迭代在類的o定義的類 - 你可能要遍歷之類的o直接的方法,即:

for (Method m : o.getClass().getMethods()) { 
    if (m.getAnnotation(EventHandler.class) != null) { 
     Class[] requiredTypes = m.getParameterTypes(); 
     if (requiredTypes.length == 1 && requiredTypes[0].isAssignableFrom(e.getClass()) { 
      m.invoke(o, e); 
     } 
    } 
} 
0

可以使用method.getGenericParameterTypes()得到一個Method的參數類型,所以:

m.invoke(clss, m.getGenericParameterTypes()[0].class.cast(e)); 

不知道如果這就是你想要的。

+0

通過查看那段代碼,您似乎嘗試將事件e轉換爲方法中所需的參數。如果不能完成,那麼它將不會被髮送? (如異常捕捉)我認爲這是我正在尋找的。非常感謝! – Limnic

+0

這就是我所做的,將事件轉換爲所需的參數。您可以使用普通的try/catch塊來進行異常處理。請參閱InvocationTargetException,IllegalAccessException和ClassCastException。 – 2013-08-27 17:36:15

0

假設EventHandler批註的方法只有一個參數

Method[] methods = YourClass.class.getDeclaredMethods(); 
Object yourInstance = null; // get it 
Event e = null; // get it 
for (Method method : methods) { 
    EventHandler handler = method.getAnnotation(EventHandler.class); 
    if (handler != null) { 
     Class<?>[] parameterTypes = method.getParameterTypes(); 
     // you're going to need different logic if you have more than one parameter 
     if (parameterTypes.length == 1 && parameterTypes[0].isAssignableFrom(e.getClass())) { 
      method.invoke(yourInstance, e); 
     } 
    } 
} 

我沒有包括在任何異常處理。

獲取事件處理程序候選類的所有方法並遍歷它們。如果方法具有@EventHandler註釋,請獲取其參數類型列表。如果它只有一個參數,並且該類型可以從事件類型e.getClass()分配,則調用它傳遞事件。

0

我現在已經修改了代碼的工作事件系統!!!!!! :D非常感謝andersschuller!

public void fireEvent(Event e) { 

    Method[] methods; 

    for (Object o : eventContainers) { 
     methods = o.getClass().getMethods(); 
     for (Method m : methods) { 
      if (m.getAnnotation(EventHandler.class) != null) { 
       try { 

        if (m.getParameterTypes()[0].isAssignableFrom(e.getClass())) { 
         m.invoke(o, e); 
        } 
       } catch (IllegalAccessException ex) { 
       } catch (IllegalArgumentException ex) { 
       } catch (InvocationTargetException ex) { 
       } 
      } 
     } 
    } 
} 

我記住了所有的答案,謝謝大家!