Java獲取匿名類對象(通過new接口、抽象類等方式創建)的方法上的註解

匿名類對象,最常見的是通過直接new一個接口,並實現接口中的方法來創建。在註冊swing或者swt控件的事件監聽器的時候,我們經常通過創建匿名對象的方式避免創建新的類來繼承Adapter抽象類或者實現Listener接口,例如:

NewGame.addSelectionListener(new SelectionAdapter() { // SelectionAdapter是一個抽象類
	@Override
	public void widgetSelected(SelectionEvent e) {
	}
});

現在假設有這樣的代碼,我希望在匿名類對象的方法上,添加一個名爲@EventMusic的註解,可以標記事件發生的時候的音樂。可以寫這樣的代碼:

public class MainWin extends ApplicationWindow {
  
	// ……  

	public static void main(String args[]) {  
		Class<?> cmw = MainWin.class;  
		Field ngc = cmw.getDeclaredField("NewGameClick");  
		Class<?> cListener = ngc.getType();  
		Method[] methods = cListener.getMethods();  
		for(Method method : methods)  
		{  
			Annotation[] anns = method.getAnnotations();  
			for(Annotation ann : anns) System.out.println(ann.toString());  
		}  
	}  
	
    private SelectionListener NewGameClick = new SelectionAdapter() {  
        @Override  
        @EventMusic("doubleclick")  
        public void widgetSelected(SelectionEvent arg0) {  
            System.out.println("public void widgetSelected(SelectionEvent) 執行");  
        }  
    };  
}  

註解@EventMusic的代碼:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface EventMusic{
	public String value();
}

我們知道,註解的定義本身不實現任何的功能,註解的功能都是靠其他代碼通過反射檢測到註解之後,再根據註解的含義去做相應的處理的。所以,main函數的代碼嘗試去獲取NewGameClick對象裏的所有方法上的註解,然後把名稱輸出出來。遺憾的是,這樣的代碼你永遠都看不到輸出的內容裏有@EventMusic,明明@EventMusic的定義裏設置了@Retention(RetentionPolicy.RUNTIME),也就是在運行時能夠通過反射檢測到,爲什麼又會拿不到呢?


其實原理很簡單,上面的main函數的代碼,獲取的不是NewGameClick對象的widgetSelected方法上的註解,而是SelectionListener接口的widgetSelected方法上的註解。那位說,這不一樣嗎?答:不一樣。如果SelectionListener是一個類,NewGameClick是它的對象,那麼上面main函數的代碼沒問題。但是SelectionListener是接口,NewGameClick是它的一個匿名類對象,差別就在這裏。SelectionListener是一種類型;而創建出對象的匿名類,即使是匿名的,也是與SelectionListener是不一樣的類型,也就是說SelectionListener.class.equals(NewGameClick.getClass())的結果是flase。可以用如下的代碼檢驗:

MainWin window = new MainWin();
Class<?> cmw = MainWin.class;
Field ngc = cmw.getDeclaredField("NewGameClick");
Class<?> cListener1 = ngc.getType();
Class<?> cListener2 = ngc.get(window).getClass();  // 取回NewGameClick這個對象再調用它的getClass方法
Class<?> cListener3 = SelectionListener.class;

boolean one_two = cListener1.equals(cListener2);   // false
boolean one_three = cListener1.equals(cListener3); // true

這時候就很明白了,遇到匿名類對象,必須通過它的getClass方法才能拿到匿名類的Class,通過Field.getType獲取到的只是原來的接口或者抽象類的Class,所以用原來的接口或者抽象類的Class又怎麼可能拿到匿名類的方法的註解呢。最後給出可以拿到@EventMusic的代碼:

public class MainWin extends ApplicationWindow {
	// ……
	
	public static void main(String args[]) {
		MainWin window = new MainWin();
		Class<?> cmw = MainWin.class;
		Field ngc = cmw.getDeclaredField("NewGameClick");
		Class<?> cListener = ngc.get(window).getClass(); // 拿到對象再調用對象的getClass獲取匿名類的Class
		Method[] methods = cListener.getMethods();
		for(Method method : methods)
		{
			Annotation[] anns = method.getAnnotations();
			for(Annotation ann : anns) System.out.println(ann.toString());
		}
	}
	
	private SelectionListener NewGameClick = new SelectionAdapter()  {
		@Override
		@EventMusic("doubleclick")
		public void widgetSelected(SelectionEvent arg0) {
			System.out.println("public void widgetSelected(SelectionEvent) 執行");
		}
	};
}






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章