类型注解与重复注解一些限制

类型注解

原来注解只能用于声明,从JDK 8开始,注解还可以用于大多数使用类型的地方,这种注解称为类型注解。类型注解允许工具对代码执行额外的检查,从而帮助避免错误。javac本身一般不执行这些检查,这种工具需要作为编译器插件发挥作用。

例子

@Target(ElementType.TYPE_USE)
public @interface TypeAnno {
}

@Target(ElementType.TYPE_USE)
public @interface MaxLen {

    int value();

}

@Target(ElementType.TYPE_USE)
public @interface Unique {
}

@Target(ElementType.TYPE_PARAMETER)
public @interface What {

    String description();

}

@Target(ElementType.FIELD)
public @interface NotNull {
}

@Target(ElementType.METHOD)
public @interface Recommended {
}

// 注解类型参数
public class TypeTest<@What(description = "Generic data type") T> {

    // 注解字段类型
    private @TypeAnno String first;

    // 注解字段声明
    private @NotNull String second;

    // 注解数组元素类型
    private @TypeAnno Integer[] nums;

    // 注解数组级别
    private String @MaxLen(5) [] @MaxLen(10) [] data;

    // 注解构造函数
    public @Unique TypeTest() {
    }

    // 注解this的类型
    public int f1(@TypeAnno TypeTest<T> this, int x) {
        return 10;
    }

    // 注解方法返回类型
    public @TypeAnno Integer f2(int j, int k) {
        return j + k;
    }

    // 注解方法声明
    public @Recommended Integer f3(String str) {
        return str.length() / 2;
    }

    // 注解异常类型
    public void f4() throws @TypeAnno NullPointerException {
        // ...
    }

    public static void test() {
        TypeTest<@TypeAnno Integer> obj1 = new TypeTest<>(); // 注解实际类型参数
        @TypeAnno TypeTest<Integer> obj2 = new @TypeAnno TypeTest<>(); // 注解实例类型

        Object x = new Integer(10);
        Integer y;

        y = (@TypeAnno Integer) x; // 注解强制类型转换
    }

}

// 注解被继承的类型
public class SubTypeTest extends @TypeAnno TypeTest<Integer> {
    // ...
}

注意

  • 类型注解必须包含ElementType.TYPE_USE作为目标。
  • 类型注解需要放到应用该注解的类型前面。
  • 不能对void返回类型添加注解。
  • @Target可以用于消除声明注解和类型注解的模糊性问题,比如上面例子中@TypeAnno注解字段类型而@NotNull注解字段声明;@TypeAnno注解方法返回类型而@Recommended注解方法声明。

this对象

this是所有实例方法的隐式参数,它的类型必须是其类的类型。类型注解可以注解this的类型,但是需要使用JDK 8的一个新特性,从JDK 8开始可以显式地将this声明为方法的第一个参数。

除非是要注解this的类型,否则没必要声明this。并且显示声明this没有改变方法签名,因为默认也会隐式声明this。


重复注解

JDK 8新增了另一种注解特性,这种特性称为重复注解,它允许在相同元素上重复应用同一个注解。

@Repeatable注解

可重复的注解必须用@Repeatable进行注解。@Repeatable定义在java.lang.annotation中,它的value域指定了重复注解的容器类型。源码如下所示:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {

    /**
     * Indicates the <em>containing annotation type</em> for the
     * repeatable annotation type.
     * @return the containing annotation type
     */
    Class<? extends Annotation> value();
    
}

容器注解

重复注解的容器类型被指定为注解。容器注解的value域是重复注解类型的数组。要创建重复注解,必须创建容器注解,然后将该容器注解的类型作为@Repeatable注解的参数。

@Retention(RetentionPolicy.RUNTIME)
@Repeatable(RepeatedAnnos.class)
public @interface RepeatableAnno {

    String name() default "test";

    int value() default 100;

}

@Retention(RetentionPolicy.RUNTIME)
public @interface RepeatedAnnos {

    RepeatableAnno[] value();

}

获取重复注解的方式

  • 使用getAnnotation()方法获取重复注解,要使用容器注解作为参数,而不是重复注解。
public class RepeatableTest {

    @RepeatableAnno(name = "first annotation", value = 10)
    @RepeatableAnno(name = "second annotation", value = 20)
    public static void test() {
        RepeatableTest obj = new RepeatableTest();

        try {
            Class<?> c = obj.getClass();
            Method m = c.getMethod("test");
            Annotation anno = m.getAnnotation(RepeatedAnnos.class);
            System.out.println(anno);
        } catch (NoSuchMethodException e) {
            System.out.println("Method Not Found.");
        }
    }

    public static void main(String[] args) throws Exception {
        test();
    }

}

上面例子的输出如下:
@RepeatedAnnos(value=[@RepeatableAnno(name=first annotation, value=10), @RepeatableAnno(name=second annotation, value=20)])

  • 获取重复注解的另一种方式是使用JDK 8添加到AnnotatedElement中的新方法,它们能够直接操作重复注解。这些方法包括getAnnotationsByType()和getDeclaredAnnotationsByType()。
Annotation[] annos = m.getAnnotationsByType(RepeatableAnno.class);
for (Annotation anno : annos) {
    System.out.println(anno);
}

一些限制

使用注解声明有一些限制:

  • 一个注解不能继承另一个注解。
  • 注解声明的成员方法不能带参数。
  • 注解声明的成员方法不能指定throws子句。
  • 注解不能被泛型化。



 

 

Java8对注解处理提供了两点改进:可重复的注解及可用于类型的注解

1.可重复注解

Java8以前定义注解:

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "mobei";
}

使用:


 
image.png

可以看到这时当做重复注解使用就报错了。
如果要使用重复注解,必须先定义一个容器:

//注解容器
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[] value();
}

在重复注解上标注@Repeatable注解并指定容器类:

//使用可重复注解:①必须使用@Repeatable注解标注该注解②在@Repeatable注解中指定该注解的注解容器
@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})//TYPE_PARAMETER:J8中新增加的类型注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "mobei";
}

使用:


 
image.png

不报错,测试:

    @Test
    public void test1() throws Exception {
        Class<TestAnnotation> clazz = TestAnnotation.class;
        Method m1 = clazz.getMethod("show");
        MyAnnotation[] mas = m1.getDeclaredAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation myAnnotation : mas) {
            System.out.println(myAnnotation.value());
        }
    }

输出:

Hello
World
2.类型注解

在上面的MyAnnotation的@Target注解中加入TYPE_PARAMETER

//使用可重复注解:①必须使用@Repeatable注解标注该注解②在@Repeatable注解中指定该注解的注解容器
@Repeatable(MyAnnotations.class)
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, TYPE_PARAMETER})//TYPE_PARAMETER:J8中新增加的类型注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "mobei";
}

可以修饰参数:

@MyAnnotation("Hello")
@MyAnnotation("World")
public void show(@MyAnnotation("abc") String str){
}

 

posted @ 2019-08-25 17:43  牧之丨  阅读(886)  评论(0编辑  收藏  举报