有没有一种方法可以将Java注解作为参数传递?

35

大家好,有没有一种方法可以直接将注解作为参数传递(而不是通过反射来完成所有开销)?例如,在下面的代码中,我有一个保存int值的注解Number,我想将其作为参数传递给addImpl方法,除了通过反射方式外,我该如何实现?

代码片段:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD})
public @interface Number {
    int value();
}

public void add(int x2) {
    addImpl(@Number(value = 10) lol, x2);
}

public void addImpl(Number a, int b) {
    System.out.println(a.value() + b);
}

public static void main(String[] args) {
    new TestClass().add(3);
}

4
为什么将“Number”定义为注解而不是普通类?注解的目的是在编译时静态地附加元数据。如果这不是你的目标,那么为什么它被定义为注解? 答:将“Number”定义为注解是为了将元数据与代码中使用的数字相关联,以便在编译时进行验证和优化。此外,通过将其定义为注解,开发人员可以更轻松地将自定义注解与“Number”一起使用。 - jthg
数字只是我想出来的一个示例,我这样做是为了使协议实现更容易 ;) - Marcos Roriz Junior
5个回答

25

是的,您可以像使用普通接口一样传递注解。

唯一无法做到的是在运行时创建该接口的实例。您只能获取现有的注解并将它们传递。

import java.lang.annotation.*;

public class Example {

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public static @interface Number {
        int value();
    }

    @Number(value = 42)
    public int adder(final int b) throws SecurityException, NoSuchMethodException {
        Number number = getClass().getMethod("adder", int.class).getAnnotation(Number.class);
        return addImpl(number, b);
    }

    public int addImpl(final Number a, final int b) {
        return a.value() + b;
    }

    public static void main(final String[] args) throws SecurityException, NoSuchMethodException {
        System.out.println(new Example().adder(0));
    }
}

1
好的 - 这个有一些实际应用场景吗? - Andreas Dolk
2
@Andreas_D:当你在某个注释上做出反应时,你可以很容易地重构代码,使其在多个方法(甚至类)中执行其任务,并且能够传递注释非常有用。 - Joachim Sauer
3
另一个用例可能是测试JSR 303验证器。它具有一个名为initialize()的方法,该方法接受注释作为参数。 - Oleksandr.Bezhan
如果不是完全错误的话,这个答案是误导性的。“唯一不能做的就是在运行时创建该接口的实例。” - 好吧,这是显而易见的,因为你永远无法创建接口的实例。然而,正如@AndreiFierbinteanu在(正确的)答案中指出的那样,您可以创建一个实现注释接口的(可能是匿名的)类,然后传递该类的实例。而且,正如其他人指出的那样,在基于注释的框架上工作时,有很多用例需要这样做。 - raner

12

你可以这样做:

public void add(int x2) {
    addImpl(new Number() {

        @Override
        public int value() {
            return 10;
        }

        @Override
        public Class<? extends Annotation> annotationType() {
            return Number.class;
        }
    }, x2);
}

由于Number基本上是一个接口,您必须创建一个实现该接口的匿名类的实例,并将其传递给方法。

虽然为什么要这样做超出了我的理解。如果您需要向某个东西传递值,应该真正使用一个类。


这个数字只是一个示例 :3 - Marcos Roriz Junior
请注意,此实现对于特定用例已足够好,但对于其他用途(如CDI限定符),应根据注释文档实现equalshashcode - Xavier Dury

4
据我所知,在您的add实现中,不存在所谓的“注释字面量”。
我认为最接近这个概念的是声明该方法以接受类型为java.lang.annotation.Annotation的参数 - 但是您仍然需要通过反射从类/方法对象中获取这些实例。

2
如果你需要在测试中传递注释,可以对其进行模拟。 例如,JSR 303验证器的测试可能如下所示:
public void test() {
    final TextLengthValidator validator = new TextLengthValidator();
    validator.initialize(mock(TextLength.class));
    final boolean valid = validator.isValid("some text", mock(ConstraintValidatorContext.class));
    assertThat(valid, is(true));
}

2

数字(Number)也是一个老旧的接口,您可以实现一个具体类。

伙计们,这非常有用。虽然模块大多处理编译时固定的注释,但有时我们需要从其他来源(如xml、gush!)获取其他信息并将其馈送到模块中。我们可以过度设计,或者简单地创建一个注释类型的运行时对象。


1
在自己的类中实现注解接口至少是一种代码异味。 - Joachim Sauer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接