【问题标题】:Creating Callables using Annotation使用注解创建 Callables
【发布时间】:2013-07-24 11:15:32
【问题描述】:

我正在尝试制作一个类似于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() : "";
        }
    });

还有更多。

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

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

【问题讨论】:

  • 你可以有一个包含很多方法的类,比如@PatternHandler("foo") String someMethodName(String match, LocalTownyChatEvent event) {},这些方法是通过该注释注册的,你可以保存replacer.registerFormatReplacement( 部分。要么需要反射才能将每个注册为Method,或者您需要将每个注册为Callable
  • 这是我的想法,但我的问题是“要么需要反射才能将每个注册为方法,或者你需要将每个都设为可调用”我不知道如何开始那部分。我还计划在某个阶段使用 Annotation 处理器来生成文档,所以这是主要原因。

标签: java annotations apt


【解决方案1】:

假设你写了一个小注解

@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;
        }
    };
}

现在您可以获取包含这些静态字段的整个类甚至多个类,并将其传递给某个注册表方法,该方法在该类中的每个字段上进行反射式迭代,如果它是一个带注释的可调用寄存器,则它会进行注册。

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();
                }
            }
        }
    }
}

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

此处为完整示例:http://ideone.com/m3PPcY

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

此外,与字段相同的方法将适用于编写更少代码的方法:

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

Registry 然后会查找所有Methods。然后例如将它们包装在

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";
        }
    }
}

然后像往常一样继续。但请注意:反射调用方法可能比直接通过代码调用要慢 - 仅占百分之几,无需担心

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

【讨论】:

  • 谢谢,其他人一直告诉我只是通过反射来调用这些方法,但我的印象是大多数时候应该避免这种情况,猜想这是它真正有用的时候之一。
  • @RyanTheLeach 任何可以帮助您编写更简洁/更少代码的东西都不应避免。显然有一些缺点,比如失去一些通常的编译时类型安全,因为检查必须在运行时完成,performance,初学者不理解的奇怪代码,......但它完全值得 IMO它。
【解决方案2】:

您可以尝试新的 Java 8 Lambda 表达式 (http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)。

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("{worldname}"), 
  (match, event) -> { return String.format(ChatSettings.getWorldTag(), event.getEvent().getPlayer().getWorld().getName()); } 
});

你也可以用另一个接口、方法等来进一步推动它

【讨论】:

  • 不幸的是,这不是一个选择。卡在 java 6 上。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-03-19
  • 2019-07-22
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 2016-07-22
相关资源
最近更新 更多