Java Votary

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            48 隨筆 :: 1 文章 :: 80 評論 :: 0 Trackbacks

          Tiger 中的注釋,第 1 部分: 向 Java 代碼中添加元數據

          如何使用 Java 5 的內置注釋

          developerWorks
          文檔選項
          將此頁作為電子郵件發送

          將此頁作為電子郵件發送

          未顯示需要 JavaScript 的文檔選項


          對此頁的評價

          幫助我們改進這些內容


          級別: 初級

          Brett McLaughlin, 作者/編者, O'Reilly Media, Inc

          2004 年 9 月 01 日

          注 釋,J2SE 5.0 (Tiger) 中的新功能,將非常需要的元數據工具引入核心 Java 語言。該系列文章分為兩部分,在這第 1 部分中,作者 Brett McLaughlin 解釋了元數據如此有用的原因,向您介紹了 Java 語言中的注釋,并研究了 Tiger 的內置注釋。

          編程的一個最新的趨勢,尤其是在 Java 編程方面,是使用 元數據。簡單地說,元數據就是 關于數據的數據。元數據可以用于創建文檔,跟蹤代碼中的依賴性,甚至執行基本編譯時檢查。許多元數據工具,如 XDoclet(請參閱 參考資料),將這些功能添加到核心 Java 語言中,暫時成為 Java 編程功能的一部分。

          直到可以使用 J2SE 5.0(也叫做 Tiger,現在是第二個 beta 版本),核心 Java 語言才最接近具有 Javadoc 方法的元數據工具。您使用特殊的標簽集合來標記代碼,并執行 javadoc 命令來將這些標簽轉化成格式化的 HTML 頁面,該頁面說明標簽所附加到的類。然而,Javadoc 是有缺陷的元數據工具,因為除了生成文檔之外,您沒有固定、實用、標準化的方式來將數據用于其他用途。HTML 代碼經常混入到 Javadoc 輸出中這一事實甚至更進一步降低了其用于任何其他目的的價值。

          Tiger 通過名為 注釋的新功能將一個更通用的元數據工 具合并到核心 Java 語言中。注釋是可以添加到代碼中的修飾符,可以用于包聲明、類型聲明、構造函數、方法、字段、參數和變量。Tiger 包含內置注釋,還支持您自己編寫的定制注釋。本文將概述元數據的優點并向您介紹 Tiger 的內置注釋。本系列文章的 第 2 部分將研究定制注釋。我要感謝 O'Reilly Media, Inc.,他們非常慷慨地 允許我在本文中使用我關于 Tiger 的書籍的“注釋”一章中的代碼示例(請參閱 參考資料)。

          元數據的價值


          一 般來說,元數據的好處分為三類:文檔編制、編譯器檢查和代碼分析。代碼級文檔最常被引用。元數據提供了一種有用的方法來指明方法是否取決于其他方法,它們 是否完整,特定類是否必須引用其他類,等等。這確實非常有用,但對于將元數據添加到 Java 語言中來說,文檔編制可能是 最不相關的理由。Javadoc 已經提供了非常容易理解和健壯的方法來文檔化代碼。另外,當已經存在文檔編制工具,并且在大多數時候都工作得很好時,誰還要編寫文檔編制工具?
          不要漏掉本系列的另一部分

          一定要閱讀本系列的“ 第 2 部分”,該部分研究了定制注釋。

          編譯器檢查


          元數據更重要的優點是編譯器可以使用它來執行基本的編譯時檢查。例如,您將在本文后面的 Override 注釋中 看到 Tiger 引入了一個這樣的注釋,用于允許您指定一種方法覆蓋超類中的另一種方法。Java 編譯器可以確保在元數據中指明的行為實際發生在代碼級別。如果從來沒有找出過這種類型的 bug,這樣做似乎有點傻,但是大多數年齡很大的 Java 編程老手都曾經花費至少多個晚上來查明他們的代碼為什么不能用。當最后認識到方法的參數有錯,且該方法實際上 沒有 覆蓋超類中的方法時,您可能更感到難受。使用元數據的工具有助于輕松地查明這種類型的錯誤,從而可以節省那些晚上來看長期進行的 Halo 聯賽。
          JSR 175

          JSR 175, Java 編程語言的元數據工具,為將元數據合并到核心 Java 語言中提供了正式理由和說明(請參閱 參考資料)。根據 JSR,注釋“不直接影響程序的語義。然而,開發和部署工具可以讀取這些注釋,并以某種形式處理這些注釋,可能生成其他 Java 編程語言源文件、XML 文檔或要與包含注釋的程序一起使用的其他構件。”

          代碼分析


          可 以證明,任何好的注釋或元數據工具的最好功能就是可以使用額外數據來分析代碼。在一個簡單的案例中,您可能構建代碼目錄,提供必需的輸入類型并指明返回類 型。但是,您可能想,Java 反射具有相同的優點;畢竟,可以為所有這些信息內省代碼。這從表面上看似乎是正確的,但是在實際中通常不使用。許多時候,方法作為輸入接受的或者作為輸出 返回的類型實際上不是該方法想要的類型。例如,參數類型可能是 Object ,但方法可能僅使用 Integer 。這在好些情況下很容易發生,比如在方法被覆蓋而超類使用常規參數聲明方法時,還有正在進行許多序列化的系統中也容易發生。在這兩種情況中,元數據可以指示代碼分析工具,雖然參數類型是 Object ,但 Integer 才是真正需要的。這類分析非常有用,但也不能夸大它的價值。

          在 更復雜的情況下,代碼分析工具可以執行所有種類的額外任務。示例 du jour 是 Enterprise JavaBean (EJB) 組件。甚至簡單 EJB 系統中的依賴性和復雜性都非常令人吃驚。您具有了 home 接口和遠程接口,以及本地接口和本地 home 接口,還有一個實現類。保持所有這些類同步非常困難。但是,元數據可以提供這個問題的解決放案。好的工具(還是要提一下 XDoclet)可以管理所有這些依賴性,并確保無“代碼級”連接、但有“邏輯級”捆綁的類保持同步。元數據在這里確實可以發揮它的作用。



          回頁首


          注釋的基本知識


          現在已經了解了元數據的好處,我將介紹 Tiger 中的注釋。注釋采用“at”標記形式 ( @ ),后面是注釋名稱。然后在需要數據時,通過 name=value 對向注釋提供數據。每次使用這類表示法時,就是在生成注釋。一段代碼可能會有 10 個、50 個或更多的注釋。不過,您將發現多個注釋都可能使用相同的 注釋類型。類型是實際使用的結構,在特定上下文中,注釋本身是該類型的具體使用(請參閱側欄 注釋或注釋類型?)。
          注釋或注釋類型?

          是否對什么是注釋與什么是注釋類型感到迷惑?了解這個的最簡單方法就是對比所熟悉的 Java 語言概念來想。可以定義一個類(例如 Person ),則在 JVM 中將總是僅有該類的一個版本(假設沒有進行麻煩的類路徑設置)。然而,在任何給定時間,可能會使用該類的 10 個或 20 個 實例。仍然是只有一個 Person 類,但是它以不同的方式使用多次。注釋類型和注釋也是這樣。注釋類型類似于類,注釋類似于該類的實例。

          注釋分為三個基本種類:

          • 標記注釋沒有變量。注釋顯示簡單,由名稱標識,沒有提供其他數據。例如, @MarkerAnnotation 是標記注釋。它不包含數據,僅有注釋名稱。

          • 單一值注釋與標記注釋類似,但提供一段數據。因為僅提供很少的一點數據,所以可以使用快捷語法(假設注釋類型接受此語法): @SingleValueAnnotation("my data") 。除了 @ 標記外,這應該與普通的 Java 方法調用很像。

          • 完整注釋有多個數據成員。因此,必須使用更完整的語法(注釋不再像普通的 Java 方法): @FullAnnotation(var1="data value 1", var2="data value 2", var3="data value 3")

          除了通過默認語法向注釋提供值外,還可以在需要傳送多個值時使用名稱-值對。還可以通過花括號為注釋變量提供值數組。清單 1 顯示了注釋中的值數組的示例。

          清單 1. 在注釋中使用按數組排列的值

          @TODOItems({ // Curly braces indicate an array of values is being supplied
          @TODO(
          severity=TODO.CRITICAL,
          item="Add functionality to calculate the mean of the student's grades",
          assignedTo="Brett McLaughlin"
          ),
          @TODO(
          severity=TODO.IMPOTANT,
          item="Print usage message to screen if no command-line flags specified",
          assignedTo="Brett McLaughlin"
          ),
          @TODO(
          severity=TODO.LOW,
          item="Roll a new website page with this class's new features",
          assignedTo="Jason Hunter"
          )
          })

          清單 1 中的示例并沒有乍一看那樣復雜。 TODOItems 注釋類型有一個具有值的變量。這里提供的值比較復雜,但 TODOItems 的使用實際與單一值注釋類型相符,只是這里的單一值是數組而已。該數組包含三個 TODO 注釋,其中每個注釋都是多值的。逗號分隔每個注釋內的值,以及單個數組內的值。非常容易,是吧?

          但是我講的可能超前了些。 TODOItemsTODO定制注釋, 是本系列文章第 2 部分中的主題。但是我想讓您看到,即使復雜注釋(清單 1 幾乎是最復雜的注釋)也不是非常令人害怕的。當提到 Java 語言的標準注釋類型時,將很少看到如此復雜的情況。正如將在下面三個部分了解到的,Tiger 的基本注釋類型的使用都極其簡單。



          回頁首


          Override 注釋


          Tiger 的第一個內置注釋類型是 OverrideOverride 應該僅用于方法(不用于類、包聲明或其他構造)。它指明注釋的方法將覆蓋超類中的方法。清單 2 顯示了簡單的示例。 清單 2. 操作中的 Override 注釋

          package com.oreilly.tiger.ch06;

          public class OverrideTester {

          public OverrideTester() { }

          @Override
          public String toString() {
          return super.toString() + " [Override Tester Implementation]";
          }

          @Override
          public int hashCode() {
          return toString().hashCode();
          }
          }

          清單 2 應該很容易理解。 @Override 注釋對兩個方法進行了注釋 — toString()hashCode() ,來指明它們覆蓋 OverrideTester 類的超類 ( java.lang.Object ) 中的方法的版本。開始這可能看起來沒什么作用,但它實際上是非常好的功能。如果不覆蓋這些方法,根本 無法 編譯此類。該注釋還確保當您將 toString() 弄亂時,至少還有某種指示,即應該確保 hashCode() 仍舊匹配。

          當編碼到很晚且輸錯了某些東西時,此注釋類型真的可以發揮很大的作用,如清單 3 中所示。

          清單 3. 使 Override 注釋捕獲打字稿


          package com.oreilly.tiger.ch06;

          public class OverrideTester {

          public OverrideTester() { }

          @Override
          public String toString() {
          return super.toString() + " [Override Tester Implementation]";
          }

          @Override
          public int hasCode() {
          return toString().hashCode();
          }
          }

          在清單 3 中, hashCode() 錯誤地輸入為 hasCode() 。注釋指明 hasCode() 應該覆蓋方法。但是在編譯中, javac 將發現超類 ( java.lang.Object ) 沒有名為 hasCode() 的方法可以覆蓋。因此,編譯器將報錯,如圖 1 中所示。

          圖 1. 來自 Override 注釋的編譯器警告

          缺少的功能

          在單一值注釋類型中,如果 Deprecated 允許包含錯誤類型消息將更好。然后,當用戶使用聲明為過時的方法時,編譯器可以打印消息。該消息可以指明使用方法的結果如何重要,說明何時將停止方法,甚至建議備用方法。可能 J2SE 的下一版本(“Mustang”,他們這樣命名)將提供這種功能。

          這個便捷的小功能將幫助快速捕獲打字稿。



          回頁首


          Deprecated 注釋


          下一個標準注釋類型是 Deprecated 。與 Override 一樣, Deprecated 是標記注釋。正如您可能期望的,可以使用 Deprecated 來對不應再使用的方法進行注釋。與 Override 不一樣的是, Deprecated 應該與正在聲明為過時的方法放在同一行中(為什么這樣?說實話我也不知道),如清單 4 中所示。 清單 4. 使用 Deprecated 注釋

          package com.oreilly.tiger.ch06;

          public class DeprecatedClass {

          @Deprecated public void doSomething() {
          // some code
          }

          public void doSomethingElse() {
          // This method presumably does what doSomething() does, but better
          }
          }

          單獨編譯此類時,不會發生任何不同。但是如果通過覆蓋或調用來使用聲明為過時的方法,編譯器將處理注釋,發現不應該使用該方法,并發出錯誤消息,如圖 2 中所示。

          圖 2. 來自 Deprecated 注釋的編譯器警告

          注意需要開啟編譯器警告,就像是必須向 Java 編譯器指明想要普通的聲明為過時警告。可以使用下列兩個標記之一和 javac 命令: -deprecated 或新的 -Xlint:deprecated 標記。



          回頁首


          SuppressWarnings 注釋


          從 Tiger “免費”獲得的最后一個注釋類型是 SuppressWarnings 。發現該類型的作用應該不難,但是 為什么該注釋類型如此重要通常不是很明顯。它實際上是 Tiger 的所有新功能的副功能。例如,以泛型為例;泛型使所有種類的新類型安全操作成為可能,特別是當涉及 Java 集合時。然而,因為泛型,當使用集合而 沒有 類型安全時,編譯器將拋出警告。這對于針對 Tiger 的代碼有幫助,但它使得為 Java 1.4.x 或更早版本編寫代碼非常麻煩。將不斷地收到關于根本無關的事情的警告。如何才能使編譯器不給您增添麻煩?

          SupressWarnings 可以解決這個問題。 SupressWarningsOverrideDeprecated 不同, 具有變量的 — 所以您將單一注釋類型與該變量一起使用。可以以值數組來提供變量,其中每個值指明要阻止的一種特定警告類型。請看清單 5 中的示例,這是 Tiger 中通常會產生錯誤的一些代碼。

          清單 5. 不是類型安全的 Tiger 代碼

          public void nonGenericsMethod() {
          List wordList = new ArrayList(); // no typing information on the List

          wordList.add("foo"); // causes error on list addition
          }

          圖 3 顯示了清單 5 中代碼的編譯結果。

          圖 3. 來自非標準代碼的編譯器警告

          清單 6 通過使用 SuppressWarnings 注釋消除了這種問題。

          清單 6. 阻止警告

          @SuppressWarings(value={"unchecked"})
          public void nonGenericsMethod() {
          List wordList = new ArrayList(); // no typing information on the List

          wordList.add("foo"); // causes error on list addition
          }

          非常簡單,是吧?僅需要找到警告類型(圖 3 中顯示為“unchecked”),并將其傳送到 SuppressWarnings 中。

          SuppressWarnings 中變量的值采用數組,使您可以在同一注釋中阻止多個警告。例如, @SuppressWarnings(value={"unchecked", "fallthrough"}) 使用兩個值的數組。此功能為處理錯誤提供了非常靈活的方法,無需進行大量的工作。



          回頁首


          結束語

          雖 然這里看到的語法可能都是新的,但您應該知道注釋非常容易理解和使用。也就是說,與 Tiger 一起提供的標準注釋相當簡單,可以添加許多功能。元數據正日益變得有幫助,您肯定會提出非常適用于自己的應用程序的注釋類型。在本系列文章的第 2 部分,我將詳細說明 Tiger 對編寫自己的注釋類型的支持。您將了解如何創建 Java 類以及將其定義為注釋類型,如何使編譯器識別您的注釋類型,以及如何使用該類型對代碼進行注釋。我甚至會更深入地說明奇異但有用的對注釋進行注釋的任務。 您將快速熟悉 Tiger 中的這一新構造。



          回頁首


          參考資料

          • 您可以參閱本文在 developerWorks 全球站點上的 英文原文

          • 不要遺漏“ Tiger 中的注釋,第 2 部分”,即本系列文章的第 2 部分,研究了定制注釋。



          • 開放源代碼 XDoclet代碼生成引擎支持面向屬性的 Java 語言編程。



          • JSR 175,將元數據工具合并到 Java 語言中的規范,處于 Java Community Process 的提議最終草案狀態。



          • 訪問 Sun 的主頁,獲取 J2SE 5.0 的所有信息



          • 可以 下載 Tiger并自己試用。



          • John Zukowski 的系列文章 Taming Tiger 以實用的基于技巧的形式講述了 Java 5.0 的新功能。



          • 由 Brett McLaughlin 和 David Flanagan 撰寫的 Java 1.5 Tiger: A Developer's Notebook 一書 (O'Reilly & Associates; 2004),以代碼為中心、開發人員友好的形式,講述了幾乎所有的 Tiger 的最新功能 — 包括注釋。



          • developerWorksJava 技術專區 可以找到數百篇有關 Java 技術的參考資料。



          • 訪問 Developer Bookstore,獲得技術書籍的完整列表,其中包括數百本 Java 相關主題的書籍。



          • 是否對無需通常的高成本入口點(entry point)或短期評估許可證的 IBM 測試產品感興趣? developerWorks Subscription為 WebSphere?、DB2?、Lotus?、Rational? 和 Tivoli? 產品提供了低成本的 12 個月單用戶許可證,包括基于 Eclipse 的 WebSphere Studio? IDE,用于開發、測試、評估和展示您的應用程序。




          回頁首


          關于作者

          作者照片

          Brett McLaughlin 從 Logo 時代(還記得那個小三角嗎?)就開始從事計算機工作。在最近幾年里,他已經成為 Java 和 XML 社區最知名的作者和程序員之一。他曾經在 Nextel Communications 實現復雜的企業系統,在 Lutris Technologies 編寫應用程序服務器,目前在為 O'Reilly Media, Inc 撰寫和編輯 書籍

          posted on 2005-12-13 23:26 Dion 閱讀(912) 評論(0)  編輯  收藏 所屬分類: Java
          主站蜘蛛池模板: 永济市| 合作市| 丘北县| 舟山市| 图木舒克市| 巴青县| 商水县| 宁化县| 平陆县| 福鼎市| 大石桥市| 定远县| 建平县| 临高县| 荆州市| 隆林| 阳山县| 治多县| 庆元县| 陕西省| 武穴市| 龙里县| 孟连| 新平| 教育| 邹城市| 永善县| 海淀区| 乌审旗| 呼和浩特市| 安陆市| 孝感市| 怀集县| 济宁市| 招远市| 望都县| 巴中市| 长汀县| 阜康市| 平南县| 泽普县|