Successfully reported this slideshow.
Your SlideShare is downloading. ×

自訂泛型、列舉與標註

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Upcoming SlideShare
Ch02 撰寫與設定Servlet
Ch02 撰寫與設定Servlet
Loading in …3
×

Check these out next

1 of 94 Ad
Advertisement

More Related Content

Slideshows for you (20)

Advertisement

Recently uploaded (20)

Advertisement

自訂泛型、列舉與標註

  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

×