Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

自訂泛型、列舉與標註

227 views

Published on

進階自訂泛型
進階自訂列舉
使用標準標註
自訂與讀取標註

Published in: Technology
  • Be the first to comment

自訂泛型、列舉與標註

  1. 1. 1
  2. 2. 自訂泛型、列舉與標註 學習目標 • 進階自訂泛型 • 進階自訂列舉 • 使用標準標註 • 自訂與讀取標註 2
  3. 3. 使用extends與? • 在定義泛型時,可以定義型態的邊界 3
  4. 4. 4
  5. 5. 使用extends與? • 可以如下使用quick()方法: 5
  6. 6. 使用extends與? • 若extends之後指定了類別或介面後,想再 指定其它介面,可以使用&連接 6
  7. 7. 使用extends與? • 來看看在泛型中的型態通配字元? 7
  8. 8. 使用extends與? 8
  9. 9. 使用extends與? • 如果有以下程式片段,則會發生編譯錯誤: • Node<Apple>是一種Node<Fruit>嗎? 9
  10. 10. 使用extends與? • 如果B是A的子類別,而Node<B>可視為一種 Node<A>,則稱Node具有共變性 (Covariance)或有彈性的(flexible) • Java的泛型並不具有共變性,不過可以使用 型態通配字元?與extends來宣告變數,使 其達到類似共變性 10
  11. 11. 使用extends與? 11
  12. 12. 使用extends與? • 若宣告?不搭配extends,則預設為? extends Object • 這與宣告為Node<Object>不同,如果 node宣告為Node<Object>,那就真的只 能參考至Node<Object>實例了 12
  13. 13. 使用extends與? • 以下會編譯錯誤: • 以下會編譯成功: 13
  14. 14. 使用extends與? • 使用通配字元?與extends限制T的型態,就 只能透過T宣告的名稱取得物件指定給 Object,或將T宣告的名稱指定為null, 除此之外不能進行其它指定動作 14
  15. 15. 使用extends與? • Java的泛型語法只用在編譯時期檢查,執行 時期的型態資訊都是未知 – 也就是執行時期實際上只會知道是Object型態 (又稱為型態抹除) • 由於無法在執行時期獲得型態資訊,編譯器 只能就編譯時期看到的型態來作檢查 15
  16. 16. • 若使用? extends Fruit時,Node類別定 義中的T宣告之名稱,就只能作為資料的提供 者,不能被重新設定 • 如果Node類別使用T來宣告某個方法的參數 ,那麼呼叫該方法時就會編輯錯誤 • 若使用T來宣告某個方法的傳回型態,呼叫該 方法則是沒有問題的 16
  17. 17. • 如果有個List<? extends Fruit> lt = new ArrayList<>() • 那麼lt.add(fruit)就會引發編譯錯誤, 然而lt.get(0)就沒有問題 • Producer extends 17
  18. 18. 使用super與? • 如果B是A的子類別,如果Node<A>視為一種 Node<B>,則稱Node具有逆變性 (Contravariance) • Java泛型並不支援逆變性 18
  19. 19. 使用super與? • 可以使用型態通配字元?與super來宣告,以 達到類似逆變性的效果 19
  20. 20. 使用super與? • 你想設計了一個籃子,可以指定籃中置放的 物品,放置的物品會是同一種類(例如都是 一種Fruit) • 有一個排序方法,可指定 java.util.Comparator針對籃中物品進 行排序… 20
  21. 21. 使用super與? • 以下泛型未填寫部份該如何宣告? 21
  22. 22. 使用super與? • 宣告為<? extends T>嗎? 22
  23. 23. 使用super與? • 你希望可以有以下的操作: 23
  24. 24. 使用super與? • 應該宣告為<? super T> 24
  25. 25. • 當類別支援泛型,而宣告時使用了? super,T 宣告的變數可以作為消費者,也就是接收指 定的角色,不適合作為提供資料的角色 • 「Consumer super」 25
  26. 26. • 如果有個List<? super Apple> lt = new ArrayList<>() • 那麼lt.add(fruit)不會有問題,lt就像 是Fruit的消費者 • Fruit fruit = lt.get(0)或Apple apple = lt.get(0)會引發編譯錯誤 26
  27. 27. 自訂列舉 • 在7.2.3中曾經簡介過列舉型態,請先瞭解該 節內容 … 27
  28. 28. 瞭解java.lang.Enum類別 • 在7.2.3節中使用enum定義過以下的Action 列舉型態: 28
  29. 29. 瞭解java.lang.Enum類別 • enum定義了特殊的類別,繼承自 java.lang.Enum 29
  30. 30. 瞭解java.lang.Enum類別 30
  31. 31. 瞭解java.lang.Enum類別 • 7.2.3中 Action.class反編譯後的內容 …. 31
  32. 32. 32
  33. 33. 瞭解java.lang.Enum類別 • 可以透過Enum定義的name()方法取得列舉 成員名稱字串,這適用於需要使用字串代表 列舉值的場合,相當於toString()的作用, 事實上toString()也只是傳回name成員的 值 • 可透過ordinal()取得列舉int值,這適用 於需要使用int代表列舉值的場合 33
  34. 34. 瞭解java.lang.Enum類別 • 例如7.2.1的Game類別,可以如下操作 34
  35. 35. 瞭解java.lang.Enum類別 • Enum的valueOf()方法,可以傳入字串與 Enum實例,它會傳回對應的列舉實例 • 通常會透過Enum子類別的valueOf()方法, 其內部就使用了Enum.valueOf()(可觀察 先前反編譯Action列舉的程式碼) 35
  36. 36. 瞭解java.lang.Enum類別 • Enum的equals()與hashCode()基本上繼 承了Object的行為,但被標示為final: 36
  37. 37. 進階enum運用 • values()方法會將內部維護Action列舉實 例的陣列複製後傳回 • 由於是複製品,因此改變傳回的陣列,並不 會影響Action內部所維護的陣列 37
  38. 38. 進階enum運用 • 可以自行定義建構式,條件是不得為公開 (public)建構式,也不可以於建構式中呼 叫super() 38
  39. 39. 進階enum運用 • 例如原本有個interface定義的列舉常數: 39
  40. 40. 進階enum運用 40
  41. 41. 41
  42. 42. 進階enum運用 • 定義列舉時還可以實作介面,例如有個介面 定義如下: 42
  43. 43. 進階enum運用 43
  44. 44. 進階enum運用 • 可以如下執行程式: 44
  45. 45. 進階enum運用 • 定義enum時有個特定值類別本體(Value- Specific Class Bodies)語法 45
  46. 46. 進階enum運用 46
  47. 47. 進階enum運用 • 實際上,編譯器會將Action3標示為抽象類 別: • 並為每個列舉成員後的{}語法,產生匿名內 部類別,這個匿名內部類別繼承了Action3, 實作了execute()方法 … 47
  48. 48. 進階enum運用 48
  49. 49. 進階enum運用 • 以先前Priority為例,可改寫為以下: 49
  50. 50. 進階enum運用 50
  51. 51. 常用標準標註 51
  52. 52. 常用標準標註 • 如果某個方法原先存在於API中,後來不建議 再使用,可於該方法上標註@Deprecated 52
  53. 53. 常用標準標註 • 若有使用者後續又想呼叫或重新定義這個方 法,編譯器會提出警訊 .. 53
  54. 54. 常用標準標註 • 如果不想看到這個警訊,使用者可以在呼叫 該方法的場合,使用@SuppressWarnings 指定抑制deprecation的警訊產生 54
  55. 55. 常用標準標註 • 在JDK9中增強了@Deprecated,增加了 since與forRemoval兩個參數 55
  56. 56. 常用標準標註 • forRemoval預設值為false,這樣的棄用 現在被歸為一般棄用(Ordinary deprecation ), – 使用@SuppressWarnings("deprecation") 來抑制警訊 56
  57. 57. 常用標準標註 • forRemoval被設為true,表示這個API未 來將會被移除,這樣的棄用被稱為移除棄用 (Removal deprecation) – @SuppressWarnings("deprecation")不會 抑制這類警訊 – 必須使用@SuppressWarnings("removal") 來抑制 57
  58. 58. 常用標準標註 • 如果被呼叫的多個方法中,包含了一般棄用 與移除棄用 • 想同時抑制這兩種警訊,必須使用 @SuppressWarnings({"deprecation" , "removal"})。 58
  59. 59. 常用標準標註 • 如果import了某個被棄用的類別,那麼編譯 器會在import語句提出警訊 • 在JDK8或舊版JDK上無法抑制這個警訊 • 在JDK9中改進了這點,若是在使用到被棄用 類別的方法上,使用了 @SuppressWarnings("deprecation") ,那麼就不會在import語句提出警訊 59
  60. 60. 常用標準標註 • 在JDK5之後加入泛型支援,對於支援泛型的 API,建議明確指定泛型真正型態,如果沒有 指定的話,編譯器會提出警訊 60
  61. 61. 常用標準標註 • 如果不想看到這個警訊,可以使用 @SuppressWarnings指定抑制unckecked的 警訊產生: 61
  62. 62. 常用標準標註 • 有沒有可能建立List<String>[]陣列實例 ?答案是不行! • 宣告List<String>[] lists是可以的, 只是實際上不會有人這麼做 • 可以宣告List<String>[] lists是為了 支援可變長度引數 62
  63. 63. 常用標準標註 • 在JDK6中這個程式碼可以順利編譯,也不會 有任何警訊 • 如果你這麼使用: 63
  64. 64. 常用標準標註 • Java泛型語法是提供編譯器資訊,使其可在 編譯時期檢查型態錯誤 • 編譯器只能就List<String>的型態資訊, 在編譯時期幫你檢查呼叫doSome()時,傳 入的list1與list2是否為List<String> 型態 64
  65. 65. 常用標準標註 • 設計doSome()的人在實作流程時,是有可 能發生編譯器無法檢查出來的執行時期型態 錯誤 • 這類問題稱為Heap pollution 65
  66. 66. 常用標準標註 • 即使編譯器提醒身為doSome()的客戶端可 能會有這類問題發生又如何? • 在JDK7中,同樣的Util類別編譯時,會發 生以下警訊: 66
  67. 67. 常用標準標註 • 如果開發人員確定避免了這個問題,則可以 使用@SafeVarargs加以標註 67
  68. 68. 常用標準標註 • 如下呼叫Util.doSome()不會再發生警訊 68
  69. 69. 自訂標註型態 • 所有標註型態其實都是 java.lang.annotation.Annotation 子介面 – @Override型態java.lang.Override – @Deprecated型態java.lang.Deprecated – … 69
  70. 70. 自訂標註型態 • 要定義一個標註可以使用@interface 70
  71. 71. 自訂標註型態 • 設定單值標註(Single-value Annotation) 71
  72. 72. 自訂標註型態 • 標註屬性也可以用陣列形式指定 72
  73. 73. 自訂標註型態 • 在定義標註屬性時,如果屬性名稱為value, 則可以省略屬性名稱,直接指定值 • 這個標註可以使用@Ignore(value = "message")指定,也可以使用 @Ignore("message")指定 73
  74. 74. 自訂標註型態 • 以下這個標註: • 可以使用@TestClass(value = {Some.class, Other.class})指定, 也可以使用@TestClass({Some.class, Other.class})指定 74
  75. 75. 自訂標註型態 • 使用default關鍵字可以對成員設定預設值 75
  76. 76. 自訂標註型態 • 如果是Class設定的屬性比較特別,必須自 訂一個類別作為預設值 76
  77. 77. 自訂標註型態 • 如果要設定陣列預設值的話,可以在 default之後加上{} 77
  78. 78. 自訂標註型態 • 可使用java.lang.annotation.Target 限定標註使用位置,限定時可指定 java.lang.annotation.ElementType 的列舉值 78
  79. 79. 自訂標註型態 • 想將@Test8限定只能用於方法: 79
  80. 80. 自訂標註型態 • 想要將標註資料加入文件,可以使用 java.lang.annotation.Documented 80
  81. 81. 自訂標註型態 • 在定義標註時設定 java.lang.annotation.Inherited標 註,就可以讓標註被子類別繼承 81
  82. 82. 自訂標註型態 • 在JDK8出現之前,ElementType的列舉成 員,是用來限定哪個宣告位置可以進行標註 • JDK8的ElementType多了兩個列舉成員 TYPE_PARAMETER、TYPE_USE,它們是用 來限定哪個型態可以進行標註 82
  83. 83. 自訂標註型態 • 在定義@Email時,必須在@Target設定 ElementType.TYPE_PARAMETER,表示 這個標註可用來標註型態參數 83
  84. 84. 自訂標註型態 • 一個標註如果被設定為 ElementType.TYPE_USE,只要是型態名 稱,都可以進行標註 84
  85. 85. 自訂標註型態 • 以下幾個標註範例都是可以的: • 以下的標註就不合法: 85
  86. 86. 自訂標註型態 • 這可以讓你如下進行標註: 86
  87. 87. 自訂標註型態 • JDK8新增了個@Repeatable 87
  88. 88. 自訂標註型態 • 在JDK9中,ElementType多了個列舉成員 MODULE,而被加註MODULE的有 @Deprecated與@SuppressWarnings 88
  89. 89. 執行時期讀取標註資訊 • 如果希望於執行時期讀取標註資訊,可以於 自訂標註時使用 java.lang.annotation.Retention搭 配 java.lang.annotation.RetentionPo licy列舉指定… 89
  90. 90. 執行時期讀取標註資訊 • 可使用java.lang.reflect.AnnotatedElement介面 實作物件取得標註資訊 90
  91. 91. 執行時期讀取標註資訊 • Class、Constructor、Field、Method、 Package等類別,都實作了 AnnotatedElement介面 • 如果標註在定義時的 RetentionPolicy 指定為RUNTIME,就可以用Class、 Constructor、Field、Method、 Package等類別的實例,取得設定的標註資 訊 91
  92. 92. 執行時期讀取標註資訊 92
  93. 93. 93
  94. 94. 執行時期讀取標註資訊 • JDK8新增了getDeclaredAnnotation() 、getDeclaredAnnotationsByType() 、getAnnotationsByType() 94

×