2010-07-27 67 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;