使用注解创建可调用对象 [英] Creating Callables using Annotation

查看:25
本文介绍了使用注解创建可调用对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试制作一个类似于 https://github.com/ElgarL/TownyChat/blob/master/src/com/palmergames/bukkit/TownyChat/TownyChatFormatter.java

I'm attempting to make a system similar to https://github.com/ElgarL/TownyChat/blob/master/src/com/palmergames/bukkit/TownyChat/TownyChatFormatter.java

replacer.registerFormatReplacement(Pattern.quote("{worldname}"), new TownyChatReplacerCallable() {
        @Override
        public String call(String match, LocalTownyChatEvent event) throws Exception {
            return String.format(ChatSettings.getWorldTag(), event.getEvent().getPlayer().getWorld().getName());
        }
    });
    replacer.registerFormatReplacement(Pattern.quote("{town}"), new TownyChatReplacerCallable() {
        @Override
        public String call(String match, LocalTownyChatEvent event) throws Exception {
            return event.getResident().hasTown() ? event.getResident().getTown().getName() : "";
        }
    });

还有更多.

有没有办法使用注解来减少重复代码的数量,避免反射调用调用方法,并且只在注册时使用它(如果有的话)?

Is there a way to use annotations to cut down on the amount of repeated code, avoiding reflection to call the call method, and only using it during registration, if at all?

我并不反对创建注释预处理器的想法,因为我已经计划这样做以启用自动生成文档.

I'm not adverse to the idea of creating an annotation pre processor as I was already planning on doing this to enable automatically generating documentation.

推荐答案

假设你写了一个小注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface PatternHandler {
    String value();
}

并创建一个类似

class Callables {

    @PatternHandler("foo")
    public static final TownyChatReplacerCallable FOO = new TownyChatReplacerCallable() {
        @Override
        public String call(String match, String event) {
            return "This is foo handler called with " + match + "," + event;
        }
    };

    @PatternHandler("bar")
    public static final TownyChatReplacerCallable BAR = new TownyChatReplacerCallable() {
        @Override
        public String call(String match, String event) {
            return "This is foo handler called with " + match + "," + event;
        }
    };
}

现在,您可以将整个类甚至包含这些静态字段的多个类传递给某个注册表方法,该方法会反射性地迭代该类中的每个字段,如果它是带注释的可调用寄存器,则该方法会注册该字段.

Now you can take the whole class or even multiple classes that contain those static fields and pass it to some registry method that iterates reflectively over each field in that class and if it's an annotated callable registers that.

class AnnotationRegistry {
    public static void register(String pattern, TownyChatReplacerCallable handler) {}

    public static void register(Class<?> clazz) {
        // only fields declared by this class, not inherited ones (static fields can't be inherited)
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            // must have that annotation
            PatternHandler annotation = field.getAnnotation(PatternHandler.class);
            if (annotation != null) {
                // must be static
                if (!Modifier.isStatic(field.getModifiers())) {
                    System.out.println("Field must be static:" + field.getName());
                    continue;
                }
                // get content of that field
                try {
                    Object object = field.get(null);
                    // must be != null and a callable
                    if (object instanceof TownyChatReplacerCallable) {
                        register(annotation.value(), (TownyChatReplacerCallable) object);
                    } else {
                        System.out.println("Field must be instanceof TownyChatReplacerCallable:"  + field.getName());
                    }
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

这将为您节省一些代码,并且在运行时不会有速度劣势,因为无需使用反射来调用这些可调用对象.

That would save you a bit code and would have no speed disadvantage at runtime since there is no need to use reflection to call those callables.

完整示例:http://ideone.com/m3PPcY

除了使用静态字段之外,如果您将类的实例传递给注册表,您也可以使用非静态字段,然后将其用作 Object object = field.get(instance); 代替null.

Besides using static fields, you can also use non static ones if you pass an instance of a class to the registry which would then be used like Object object = field.get(instance); instead of the null.

此外,相同的方法可以用于编写更少代码的方法,而不是字段:

Furthermore, instead of fields the same approach would work with methods which would be less code to write:

@PatternHandler("foo")
public static String fooMethod(String match, String event) {
    return "This is foo handler called with " + match + "," + event;
}

Registry 然后会查找所有 方法s.然后例如将它们包装在

Registry would then look for all Methods. Then for example wrap them in

class MethodAdapter implements TownyChatReplacerCallable {
    private final Method method;
    public MethodAdapter(Method m) {
        method = m;
    }
    @Override
    public String call(String match, String event) {
        try {
            return (String) method.invoke(null, match, event);
        } catch (Exception e) {
            e.printStackTrace();
            return "OMGZ";
        }
    }
}

然后像往常一样继续.但要注意:反射调用一个方法可能比直接通过代码调用它慢——只有百分之几,没什么可担心的

and continue as usual. But beware: invoking a method reflectively is potentially slower than calling it directly via code - few percent only, nothing to worry about

方法的完整示例:http://ideone.com/lMJsrl

这篇关于使用注解创建可调用对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆