2010-07-27 77 views
34

什么是实现注释的有效用例?用例实现注释

当主要设计基于注释的配置系统时,我偶尔需要创建实现代码生成或编程配置注释的类。

另一种方法涉及将注释中包含的数据镜像到DTO中,这看起来像是开销。

下面是一个例子:

public enum IDType { 
    LOCAL, 
    URI, 
    RESOURCE; 
} 

@Documented 
@Target({ METHOD, FIELD }) 
@Retention(RetentionPolicy.RUNTIME) 
@Inherited 
public @interface Id { 
    /** 
    * @return 
    */ 
    IDType value() default IDType.LOCAL; 
} 

与实施

public class IdImpl implements Id{ 

    private final IDType idType; 

    public IdImpl(IDType idType){ 
     this.idType = idType; 
    } 

    @Override 
    public IDType value() { 
     return idType; 
    } 

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

} 

我得到这个编译器警告,但似乎是许多使用情况的有效工具。

上述用于实施例中的警告是

注释类型ID不应当被用来作为IdImpl

编辑的一个超接口 :

我刚发现从Guice这个例子中:

bind(CreditCardProcessor.class) 
    .annotatedWith(Names.named("Checkout")) 
    .to(CheckoutCreditCardProcessor.class); 

请参阅Javadoc from Names

有没有人有一些信息,为什么这个限制存在或有其他一些用例?

+2

你会得到什么警告? – djna 2010-07-27 08:50:44

+2

@djina:是的,每一次你必须说一次就会有一分钱,你会变得富有。它永远不会令我惊叹。 – musiKk 2010-07-27 08:56:09

+0

也许这只是我,但它似乎与这个问题密切相关:http:// stackoverflow。com/questions/1624084/why-is-not-possible-to-extend-in-java- – 2010-07-27 18:22:02

回答

19

我从来没有在实践中使用它,但你得到的是,你可以使用类作为替代你的注释。

让我们来创建一个人造的例子。假设我们有一个文档生成器。它从给定类读取@Docu注释并打印description属性。就像这样:

import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.util.ArrayList; 
import java.util.List; 

public class DokuGenerator { 

    public static void main(String[] args) throws Exception { 
     new DokuGenerator(StaticClass.class, StaticClass2.class); 
    } 

    public DokuGenerator(Class<?>... classesToDokument) throws Exception { 
     List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument); 
     printDocumentation(documentAnnotations); 
    } 

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument) 
      throws Exception { 
     List<Docu> result = new ArrayList<Docu>(); 
     for (Class<?> c : classesToDokument) 
      if (c.isAnnotationPresent(Docu.class)) 
       result.add(c.getAnnotation(Docu.class)); 
     return result; 
    } 

    private void printDocumentation(List<Docu> toDocument) { 
     for (Docu m : toDocument) 
      System.out.println(m.description()); 
    } 

} 

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@interface Docu { 
    String description(); 
} 

@Docu(description = "This is a static class!") 
class StaticClass { 
} 

@Docu(description = "This is another static class!") 
class StaticClass2 { 
} 

打印:

This is a static class! 
This is another static class! 

现在我们要做到的是,一个类不仅可以staticly注释,但可以添加运行时信息的文档。我们很高兴在大多数时间使用@Docu注释,但有些特殊情况我们需要特殊文档。我们可能想为某些方法添加性能文档。我们可以通过让类实现注释来做到这一点。生成器首先检查注释,如果不存在,它会检查类是否实现了注释。如果是这样,它会将该类添加到注释列表中。

这样的(只有两个代码附加线路中的发电机):

import java.lang.annotation.Annotation; 
import java.lang.annotation.ElementType; 
import java.lang.annotation.Retention; 
import java.lang.annotation.RetentionPolicy; 
import java.lang.annotation.Target; 
import java.util.ArrayList; 
import java.util.Arrays; 
import java.util.List; 

public class DokuGenerator { 

    public static void main(String[] args) throws Exception { 
     new DokuGenerator(StaticClass.class, StaticClass2.class, 
       DynamicClass.class); 
    } 

    public DokuGenerator(Class<?>... classesToDokument) throws Exception { 
     List<Docu> documentAnnotations = getDocumentAnnotations(classesToDokument); 
     printDocumentation(documentAnnotations); 
    } 

    private List<Docu> getDocumentAnnotations(Class<?>... classesToDokument) 
      throws Exception { 
     List<Docu> result = new ArrayList<Docu>(); 
     for (Class<?> c : classesToDokument) 
      if (c.isAnnotationPresent(Docu.class)) 
       result.add(c.getAnnotation(Docu.class)); 
      else if (Arrays.asList(c.getInterfaces()).contains(Docu.class)) 
       result.add((Docu) c.newInstance()); 
     return result; 
    } 

    private void printDocumentation(List<Docu> toDocument) { 
     for (Docu m : toDocument) 
      System.out.println(m.description()); 
    } 

} 

@Target(ElementType.TYPE) 
@Retention(RetentionPolicy.RUNTIME) 
@interface Docu { 
    String description(); 
} 

@Docu(description = "This is a static class!") 
class StaticClass { 
} 

@Docu(description = "This is another static class!") 
class StaticClass2 { 
} 

class DynamicClass implements Docu { 

    public DynamicClass() { 
     try { 
      Thread.sleep((long) (Math.random() * 100)); 
     } catch (InterruptedException e) { 
      // ignore exception to make debugging a little harder 
     } 
    } 

    @Override 
    public String description() { 
     long millis = System.currentTimeMillis(); 
     new DynamicClass(); 
     millis = System.currentTimeMillis() - millis; 
     return "This is a dynamic class. I run on " 
       + System.getProperty("os.name") 
       + ". The construction of an instance of this class run for " 
       + millis + " milliseconds."; 
    } 

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

} 

输出是:

This is a static class! 
This is another static class! 
This is a dynamic class. I run on Windows XP. The construction of an instance of this class run for 47 milliseconds. 

你没有带更改代码生成那么多,因为你可以使用该类作为注释的替换。

其他示例应该是使用注释或XML作为配置的框架。您可能有一个处理器可用于注释。如果您使用XML作为配置,您可以生成实现注释的类的实例,并且您的处理器可以在不改变的情况下对其进行操作! (当然还有其他方法可以达到同样的效果,但这是一种方法)

+0

感谢您的详细解答。类作为反射派生注释实例的替代品确实是一个有效的例子。注释非常方便,因为配置数据和实例化它们的动态情况是非常必要的。 – 2010-07-28 15:45:49

+0

“//忽略异常以使调试更加困难”+1 – Izmaki 2016-07-05 08:43:43

-1

没有有效的用户案例 - 编译器只是强制它,因为禁止它会很麻烦,编写编译器的人可能在非常罕见的场合需要该设施。如果您需要进行分类注释查看这篇文章,看看如何做到这一点:Why is not possible to extend annotations in Java?

Inagine一个可怜的人来后,你维护和调试代码或另一个谁需要写一个代码生成工具,并假定annotatuion类型是直截了当的还是其他人只是使用这样的注释,甚至不会梦到可能发生的事情以及如何处理它。当他发现黑客并找到消除它的方法时,他将死于疝气 - 或者等同疾病:-)注释预计是纯粹的声明性陈述,仅由与注释代码分开运行的代码工具解释,将其视为数据。

重新审视这些代码,并试图诚实地说什么东西是一种合理的罗先:

public Class<? extends Annotation> annotationType() { 
    return Id.class; 
} 

,这就是史迪威一个小东西相比,人们可以把代码。

注解不是练习黑客的地方 - 这正是编译器试图传达的。您是否确切知道注释的“实现”中的代码何时以及如何运行?包括CTOR?什么是可用的,什么不是那个时候?什么是安全的呼叫?编译器不会 - 编译器会花费相当多的静态分析来检查这种黑客的实际安全性。所以相反,它只是发出警告,以便当出现问题时人们不能责怪编译,虚拟机和其他一切。

+0

我对分类注释不感兴趣,我的用例与编译器无关。我不认为我的用例是“黑客”。 – 2010-07-28 13:43:23

+0

@ZZX,您的示例缺少javadoc,希望能够为_why?_提供答案。 – 2011-01-25 21:21:35

+0

你的过度反应真的没有保证。这里有一个用于实现注释的非常有效的用例:您想运行使用unweildly api提供元数据的注释处理器,如javax.lang.model,它试图加载类值。最好编写一个注释处理器来扫描注释并生成实际上可以对类值进行操作的注释构建器。 (javax.lang.model强制您捕获未声明的运行时异常并从类型镜像中提取类类型;生成的生成器会处理其他注释处理器中的丑陋混乱)。 – Ajax 2013-04-22 08:42:07

4

JAXBIntroductions就是一个很好的例子:它允许使用XML文件配置JAXB注释。想到两个主要的用例:配置类没有源访问权限或一个类别的不同配置。

一般来说,我认为动态实例化注释以将它们传递给框架通常是一个很好的用例。但是,如果你是这个框架的设计者,我当然会考虑两次。

0

我在创建注释时使用它,并希望通过提供默认值使其用法可选。

使用你的例子;当我处理/内省下列豆时,我想使用BeanB的默认值。

@Id 
class BeanA {} 

// No annotation 
class BeanB {} 

默认实现;

private static final Id DEFAULT_ID = new Id() { 

    @Override 
    public IDType value() { 
     return IDType.LOCAL; 
    } 

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

处理;

Id beanId = (bean.getClass().isAnnotationPresent(Id.class)) 
    ? bean.getClass().getAnnotation(Id.class) 
    : DEFAULT_ID;