2009-10-26 58 views
194

我不明白爲什麼在Java註釋中沒有繼承,就像Java類一樣。我認爲這會非常有用。爲什麼不可能在Java中擴展註釋?

例如:我想知道給定的註釋是否是驗證器。通過繼承,我可以自反地瀏覽超類來知道這個註釋是否擴展了ValidatorAnnotation。否則,我怎麼能做到這一點?

那麼,任何人都可以給我一個這個設計決定的理由嗎?

+1

請注意,順便說一句,所有註釋都會擴展'java.lang.annotation.Annotation',即任何註釋都是'instanceof',儘管這個事實沒有明確聲明。 – 2015-08-20 20:06:15

回答

148

關於爲什麼沒有這樣設計的,你可以找到在JSR 175設計常見問題,在那裏說答案的原因:

你爲什麼不支持註釋亞型(其中一個註釋類型擴展另一個)?

它使註解類型 系統變得複雜,並且使其更難以編寫「特定工具」。

...

「專用工具」 - 即查詢 稱爲註釋類型任意 外部程序的程序。例如,存根發生器 屬於此類別。 這些程序將讀取帶註釋的 類,但不會將它們加載到虛擬機的 中,但會加載 註釋接口。

所以,是的,我猜,原因是它只是吻。無論如何,看起來這個問題(以及許多其他問題)正在作爲JSR 308的一部分進行研究,您甚至可以找到一個替代編譯器,該編譯器已經由Mathias Ricken開發了此功能。

+61

嗯,也許我很愚蠢,但我認爲這是一個遺憾,不能僅僅爲了「保持簡單」而擴展註釋。至少,Java設計人員對類繼承並沒有同樣的想法:P – sinuhepop 2009-11-02 11:45:16

+5

顯然[計劃用於Java 8](http://openjdk.java.net/jeps/104) – assylias 2013-02-21 23:21:09

+2

Java 8 M7,似乎並不支持子類,分類註釋。太遺憾了。 – Ceki 2013-04-15 20:55:13

1

我也有同樣的問題。不,你不能。我做了'自律'處理,在註解中編寫屬性以尊重某些標準,因此,在獲得註釋之外,您可以'嗅探'它具有的屬性是什麼類型的註釋。

1

我能想到的一件事是可以有多個註釋。所以你可以在同一個地方添加驗證器和更具體的註釋。但我可能會誤會:)

2

從來沒有想過,但...似乎你說得對,註釋繼承設施沒有問題(至少我沒有看到它的問題)。

關於使用「驗證」註釋你的榜樣 - 你可以利用「元註釋」方法即可。即您將特定的元註釋應用於整個註釋界面。

+0

我對這個問題的回答可能遲了三年,但我發現它很有趣,因爲我發現自己在同一個地方。 – mainas 2013-05-10 04:59:22

62

可擴展註釋會有效增加指定和維護另一個類型系統的負擔。這將是一個相當獨特的類型系統,所以你不能簡單地應用OO類型範例。

考慮所有的問題,當你介紹多態性與繼承的註釋(例如,當子批註改變元註釋規格如保留?發生了什麼)

而這一切增加了對什麼用例複雜性?

你想知道給定的註釋是否屬於一個類別?

試試這個:

@Target(ElementType.ANNOTATION_TYPE) 
public @interface Category { 
    String category(); 
} 

@Category(category="validator") 
public @interface MyFooBarValidator { 

} 

正如你所看到的,你可以很容易地組和分類使用所提供的設施沒有過度疼痛註解。

因此,KISS是未將元類型系統引入Java語言的原因。

[p.s.編輯]

我只是用於演示和鑑於開放式元註釋的字符串。對於你自己給定的項目,你顯然可以使用類別類型的枚舉併爲給定的註釋指定多個類別(「多重繼承」)。請注意,該值是完全捏造的,僅用於演示目的:

@Target(ElementType.ANNOTATION_TYPE) 
public @interface Category { 
    AnnotationCategory[] category(); 
} 
public enum AnnotationCategory { 
    GENERAL, 
    SEMANTICS, 
    VALIDATION, 
    ETC 
} 

@Category(category={AnnotationCategory.GENERAL, AnnotationCategory.SEMANTICS}) 
public @interface FooBarAnnotation { 

} 

+0

不起作用將打印錯誤: '@Target(ElementType.ANNOTATION_TYPE) @Retention(RetentionPolicy.RUNTIME) @interface C {}; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @C公共@interface˚F{} 類{ @F 公共無效S(){}} @測試 public void blahTest()throws NoSuchMethodException {方法m = a.class.getMethod(「S」); System.out.println(m.isAnnotationPresent(C.class)); }' – user1615664 2017-06-20 03:53:19

+0

我們註釋了一個註釋。 #S具有註釋F.F本身由C註釋。嘗試一下。 – alphazero 2017-06-23 22:32:09

+0

是的,這是正確的。但有沒有辦法更乾淨地做,而不是閱讀Annotation代理? – user1615664 2017-06-24 07:52:04

10

在你已經用註釋有它的感覺 - 元註解。如果您使用元信息註釋註釋,那麼在許多方面等同於擴展其他界面。註解是接口,所以多態並不真正起作用,並且由於它們本質上是靜態的,所以不會有運行時動態分派。

在你的驗證器的例子中,你可以在註釋中獲取註釋類型,看看它是否有驗證器元註釋。

我可以看到繼承會有幫助的唯一用例是,如果您希望能夠通過超類型獲取註釋,但這會增加一大堆複雜性,因爲給定的方法或類型可能有兩個註釋,這意味着一個數組將不得不返回而不是一個單一的對象。

所以我認爲最終的答案是用例是深奧的,並且使更多的標準用例複雜化,使其不值得。

1

我可能會遲到三年纔回應這個問題,但是我發現它很有趣,因爲我發現自己在同一個地方。這是我的承諾。您可以將註釋視爲Enums。它們提供單向信息 - 使用它或丟失它。

我有一種情況,我想模擬GET,POST,PUT和DELETE在一個Web應用程序。我非常想擁有一個名爲「HTTP_METHOD」的「超級」註釋。後來我發現它並不重要。那麼,我必須解決在HTML表單中使用隱藏字段來識別DELETE和PUT(因爲無論如何,POST和GET都可用)。

在服務器端,我查找了名爲「_method」的隱藏請求參數。如果該值爲PUT或DELETE,則它將覆蓋關聯的HTTP請求方法。話雖如此,我是否需要擴展註釋才能完成工作並不重要。所有的註釋看起來都是一樣的,但是在服務器端他們被區別對待。

所以在你的情況下,放棄癢擴展註釋。把它們當作'標記'。他們「代表」一些信息,而不一定「操縱」某些信息。

2

Java註釋支持的設計者對Java社區造成了一些「簡化」。

  1. 沒有註釋子類型使許多複雜的註釋變得不必要的難看。人們不能簡單地在註解中擁有一個屬性,它可以擁有三件事情之一。需要有三個獨立的屬性,這會混淆開發人員,並要求運行時驗證,以確保只使用三者中的一個。

  2. 每個站點只有一個給定類型的註釋。這導致完全不必要的集合註釋模式。 @Validation和@Validations,@Image和@Images等

第二個是在Java 8中進行修復,但它太晚了。許多框架都是基於Java 5中可能實現的內容編寫的,現在這些API疣在這裏可以保持很長時間。

+0

不僅如此,它還使得定義具有遞歸嵌套屬性的屬性成爲不可能 - 這是由XML模式支持的。這使得註釋的嚴格性不如XML – user2833557 2014-09-25 10:35:26

相關問題