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) 执行");
		}
	};
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值