您的位置:首页 > 编程语言 > Java开发

Java获取匿名类对象(通过new接口、抽象类等方式创建)的方法上的注解

2017-12-30 16:24 471 查看
匿名类对象,最常见的是通过直接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) 执行");
}
};
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: