【问题标题】:use of custom annotations使用自定义注释
【发布时间】:2015-09-15 04:27:28
【问题描述】:

我发现了几个与此相关的(不重复的)问题,但它们并不让我满意。

我不明白在哪里以及为什么要使用custom annotations

我在一本书中看过一个自定义注释的例子,但没有详细解释。

@interface MyAnno
{
    String str();
    int val();
}

class MyClass
{
    @MyAnno(str = "Annotation example", val = 100)
    public static void myMeth()
    {
        System.out.println("Inside myMeth()");
    }
}

class CustomAnno
{
    public static void main(String args[])
    {
        MyClass.myMeth();
    }
}

输出如预期Inside myMeth()

我对这个例子有几个问题。

1- 我如何在这个程序中使用String str()int val()?或

custom annotation 的任何抽象方法有什么用?

2- 为什么custom annotations。我的意思是它们对任何代码都有什么影响。

3- 如何创建具有 @override 效果的注释?(我的意思是任何可以注意到的效果)

如果这个例子对你没用,那么请给我一个合适的小例子,其中使用了custom annotation

【问题讨论】:

  • 您了解一般注释的用途吗?
  • @PM77-1 : 在源文件中添加补充信息
  • 你知道annotation processor是什么吗?
  • 您的示例中并未真正使用注释 - 您可能可以在网上找到更好的示例。
  • @assylias :我正在搜索一个示例,该示例具有类似覆盖的效果(如果该方法未被覆盖,则会导致编译时错误)

标签: java annotations


【解决方案1】:

注释的抽象方法定义了您可以传递给它的值(在您的情况下为str = "Annotation example", val = 100)。您可以使用反射 (Method.<T>getAnnotation(Class<T>)) 访问它们。自定义注释没有直接影响。只有在您评估它们时它们才有用。

请注意,您必须使用 @Retention(value=RUNTIME) 注释您的自定义注释,以便能够通过反射读取它。

【讨论】:

  • 自定义注释的任何示例,它至少可以做一些事情。就像@override 导致编译错误是方法没有被覆盖
  • 您应该在答案中添加注释的“保留”要求
  • 一个例子是 JUnit 的 @Test 注解。 JUnit 正在通过反射寻找带有该注解的方法,然后将它们作为测试用例执行。
【解决方案2】:

这是一个最小的例子。以下代码演示了自定义注释的使用。

这是关于员工和福利的。如果我们的要求是必须将 BasicBenefits 应用于所有类型的雇员,那么我们可以提出自定义注释,例如 BasicBenefits,并使用基本福利。

自定义注解类(接口)

import java.lang.annotation.*;
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)

@interface BasicBenefits {
    String bId() default "B-101";
    String bName() default "General Class A Employee";
}

使用自定义注解的类(不需要任何导入):

@BasicBenefits(bId="B-400", bName="General Plus Class A Employee")
public class Employee {
    String eId;
    String eName;
    public Employee(String eId, String eName){
        this.eId = eId;
        this.eName = eName;
    }

    public void getEmployeeDetails(){
        System.out.println("Employee ID: "+eId);
        System.out.println("Employee Name: "+eName);
    }
}

驱动类来测试上述内容。

import java.lang.annotation.Annotation;
public class TestCustomAnnotationBasicBenefits {
    public static void main(String[] args) throws Exception{
        Employee emp = new Employee("E-100", "user3320018");
        emp.getEmployeeDetails();
        Class reflectedClass = emp.getClass();
        Annotation hopeBenefitAnn = reflectedClass.getAnnotation(BasicBenefits.class);
        BasicBenefits bBenefits = (BasicBenefits)hopeBenefitAnn;
        System.out.println("Benefit ID: "+bBenefits.bId());
        System.out.println("Benefit Name: "+bBenefits.bName());
    }
}

您的代码看起来差不多了,只需要在 main 方法中包含两件事。

1.) 需要参考 MyClass 2.) 需要使用 MyClass 的反射获取注解。

这里有一些修改过的代码:

@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnno
{
    String str();
    int val();
}

//using above custom annotation on class level
//can also use method level
//just need to change t @Target(ElementType.METHOD)
@MyAnno(str = "Annotation example", val = 100)
class MyClass
{

    public static void myMeth()
    {
        System.out.println("Inside myMeth()");
    }
}

import java.lang.annotation.Annotation;
class CustomAnno
{
    public static void main(String args[])
    {
        //1. getting reference to the class where the custom annotation is applied.
        //2. then getting the annotation to get the values 
        MyClass myClass = new MyClass();
        Class cls = myClass.getClass();
        Annotation getMyAnno = cls.getAnnotation(MyAnno.class);
        MyAnno myAnno = (MyAnno)getMyAnno;
        MyClass.myMeth(); //left this as is.
        System.out.println("myAnno.str(): "+ myAnno.str());
        System.out.println("myAnno.str(): "+ myAnno.val());     
    }
}

【讨论】:

  • 仅打印出我们在注释中设置的内容并不会显示除文档之外的其他效果(如果有的话),这是 OP 所要求的。
  • @Nirmal,虽然这是一个很好的例子,但我没有看到任何有用的用例。你能分享你的想法吗?
  • @Andy - 这在您想通过将常用操作放在一个地方来减少代码行并仅通过注释来引用使用的情况下很有用。恕我直言,自定义注释用于组织代码并使其易于阅读。只要您对放置的某些自定义注释的用例没问题,您就不会阅读注释部分的实现细节,对吧?
  • @sinoTrinity 对此做出回应有点晚了,但我的目的是解释自定义注释的创建和使用方面,op 也暗示过询问。
【解决方案3】:

使用自定义注解的三个主要原因是:

  • 减少编写代码的工作量(编译时注释处理器为您生成代码)。这是一个教程:part 1part 2
  • 提供额外的正确性保证(编译时注释处理器会警告您有关错误)。一个很好的工具是Checker Framework,它可以防止空指针取消引用、并发错误等等。
  • 自定义行为(在运行时,您的代码使用反射检查注解,并根据注解是否存在而采取不同的行为)。 Hibernate 等框架就是这样使用注解的;另见Oracle article

在每种情况下,与其他非注释方法相比,使用注释都可以降低代码中出错的可能性。

【讨论】:

    【解决方案4】:

    为了有任何用途,必须首先解析注释。内置注解(例如@Override@FunctionalInterface,列举最明显的注解)由编译器自己解析。至于自定义注解,这些家伙通常会被第三方框架解析,虽然我们也可以使用反射机制在独立代码中演示这种技术。

    举个例子,下面的代码在运行时改变它的行为,这取决于在名为@SwitchingAnnotation的自定义注解中声明的字段的值:

    import java.lang.annotation.Target;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface SwitchingAnnotation{
        boolean flag();
    }
    
    public class Worker{
        void doThis(){ System.out.println("Doing this"); }
        void doThat(){ System.out.println("Doing that"); }
        @SwitchingAnnotation(
            flag = false
        )
        public void work(boolean flag) {
            if (flag) doThis();
            else doThat();
        }
    }
    
    class Test{
        public static void main(String[] args) {
            try{
                SwitchingAnnotation sw = Worker.class.getMethod("work", boolean.class)
                                                     .getAnnotation(SwitchingAnnotation.class);
    
                new Worker().work(sw.flag());    // prints Doing that
            }
            catch(NoSuchMethodException nsme){
                System.out.println(nsme);
            }
        }
    }
    

    【讨论】:

    • 这样做有什么意义呢?这不等同于拥有一个您直接从主方法变异的公共字段变量“标志”吗?只是更冗长;因此更糟。
    • @SebastianNielsen 你是绝对正确的:语义上它们很接近。事实上,正如您所记得的,在 JDK 5 中引入注解之前,人们习惯于遵循您建议的方法。但是,从类设计的角度来看,注解更安全:公共字段破坏了封装。另外,如果我们只需要为父类保留注释引起的多态行为怎么办?毕竟,非私有字段是可继承的......好吧,也许使用 Java 8 中出现的重复注解是一个更简洁、更具表现力的示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多