注释中的通用类型

时间:2015-02-21 01:37:24

标签: java generics annotations

请考虑以下代码:

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

public class AnnotationTest {

    @GenericAnnotation<String>(foo = "Test")
    public class Bar1 {

    }

    @ObjectAnnotation(foo = "Test")
    public class Bar2 {

    }

    @WorkingAnnotation(foo = "Test")
    public class Bar3 {

    }

    @Retention(RetentionPolicy.RUNTIME)
    public @interface GenericAnnotation<T> {

        public T foo();

    }

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

        public Object foo();

    }

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

        public String foo();

    }

}

Bar1根本没有编译。巨大的错误。

Bar2编译正常,但ObjectAnnotation注释赢了。

Bar3编译正常,但不允许泛型类型。


如果 - 例如 - 我正在尝试设置默认值,以防某个字段无法加载。此类可能是IntegerStringBoolean[],实际上是任何可能的类型。这意味着处理每个可能情况的整个注释。


是否有正确的方法来处理注释中的泛型类型?如果没有,有明确的原因吗?

2 个答案:

答案 0 :(得分:3)

编译器的错误消息纯粹令人困惑,因为它没有用来接受这样的语法。

类似的问题已经发布here

案例1

JLS,Section 9.6说明了注释声明的一般语法如下:

  

AnnotationTypeDeclaration:

{InterfaceModifier} @ interface Identifier AnnotationTypeBody

令牌标识符,在JLS的条款中,描述了类型的名称;无论是类,枚举,界面还是注释。

无法使用泛型类型声明注释,因为这些注释由TypeParameters引用,此处未包含此内容。

至于为什么,这会导致

案例2

查看下一个项目Section 9.6.1,我们发现了注释可以采用的类型限制:

  

在注释类型中声明的方法的返回类型必须是以下之一,否则会发生编译时错误:

     
      
  • 原始类型
  •   
  • 字符串
  •   
  • 类或类的调用(§4.5
  •   
  • 枚举类型
  •   
  • 注释类型
  •   
  • 一种数组类型,其组件类型是上述类型之一(§10.1)。
  •   

(下面将进一步讨论一些特殊情况,但与此问题无关)

这些限制导致您的第二个注释沮丧。它根本不是为了容纳所有类型的对象而设计的。它甚至不能保持原始的盒装类型!

现在,回到案例1 了一下:为什么通用注释在这个语法中是错误的?即使在发生任何类型擦除之前,GenericAnnotation基本上也可以成为每种类型对象的持有者。在 foo 属性的定义中,它与 ObjectAnnotation 完全相同。

现在,问题是,为什么会出现这种限制?通过限制注释可能包含的值,您将获得两个优点:首先,所有值都是编译时常量。在没有大量使用反射的情况下,无法将一个与运行时相关的值导入或导出注释。

这立即带来了第二个优势:没有人会试图将不纯的(副作用)代码放在注释中,这些注释可能会在应用程序生命周期的任何随机点加载。如果你可以引入任何类型的对象,它们的构造函数可能会在可能无法检测到的时间内产生任何副作用,如果使用这种技术,会增加调试的复杂性。

或者至少,这对我来说是最合乎逻辑的,因为从太阳或他们的继任者那里我都找不到官方消息。

可能的解决方法:无

遗憾的是,没有简单,富有表现力和简单的解决方法。 由于无法将进程放入@ Annotation-uses中,因此使用ObjectOutputStream和ByteArrayOutputStream将对象序列化为字节数组时,甚至无法做一些奇特的事情。

答案 1 :(得分:0)

没有通用注释可遗憾地是框架的限制。一种解决方案是在运行时有一个描述所需类和检查/转换类型的字段。