|   | 1 | 當您定義類別時,發現到好幾個類別的邏輯其實都相同,就只是當中所涉及的型態不一樣時,使用複製、貼上、取代的功能來撰寫程式只是讓您增加不必要的檔案管理困擾。 | 
                  
                          |   | 2 |  | 
                  
                          |   | 3 | 由於Java中所有定義的類別,都以Object為最上層的父類別,所以在 J2SE 5.0 之前,Java程式設計人員可以使用Object來解決上面這樣的需求,為了讓定義出來的類別可以更加通用(Generic),傳入的值或傳回的物件都是以Object為主,當您要取出這些物件來使用時,必須記得將介面轉換為原來的類型,這樣才可以操作物件上的方法。 | 
                  
                          |   | 4 |  | 
                  
                          |   | 5 | 然而使用Object來撰寫泛型類別(Generic Class)留下了一個問題,因為您必須要轉換介面,粗心的程式設計人員往往會忘了要作這個動作,或者是轉換介面時用錯了型態(像是該用Boolean卻用了Integer),要命的是,語法上是可以的,所以編譯器檢查不出錯誤,真正的錯誤要在執行時期才會發生,這時惱人的ClassCastException就會出來搞怪,在使用Object設計泛型程式時,程式人員要再細心一些、小心一些。 | 
                  
                          |   | 6 |  | 
                  
                          |   | 7 | 在J2SE 5.0之後,提出了針對泛型(Generics)設計的解決方案,要定義一個簡單的泛型類別是簡單的,直接來看個例子: | 
                  
                          |   | 8 |  | 
                  
                          |   | 9 |     * GenericFoo.java | 
                  
                          |   | 10 | {{{ | 
                  
                          |   | 11 | #!java | 
                  
                          |   | 12 | public class GenericFoo<T> { | 
                  
                          |   | 13 |     private T foo; | 
                  
                          |   | 14 |   | 
                  
                          |   | 15 |     public void setFoo(T foo) { | 
                  
                          |   | 16 |         this.foo = foo; | 
                  
                          |   | 17 |     } | 
                  
                          |   | 18 |   | 
                  
                          |   | 19 |     public T getFoo() { | 
                  
                          |   | 20 |         return foo; | 
                  
                          |   | 21 |     } | 
                  
                          |   | 22 | } | 
                  
                          |   | 23 | }}} | 
                  
                          |   | 24 |  | 
                  
                          |   | 25 | <T> 用來宣告一個型態持有者(Holder)T,之後您可以用 T 作為型態代表來宣告變數(參考)名稱,然後您可以像下面的程式來使用這個類別: | 
                  
                          |   | 26 | {{{ | 
                  
                          |   | 27 | GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>(); | 
                  
                          |   | 28 | GenericFoo<Integer> foo2 = new GenericFoo<Integer>(); | 
                  
                          |   | 29 |   | 
                  
                          |   | 30 | foo1.setFoo(new Boolean(true)); | 
                  
                          |   | 31 | Boolean b = foo1.getFoo(); | 
                  
                          |   | 32 |   | 
                  
                          |   | 33 | foo2.setFoo(new Integer(10)); | 
                  
                          |   | 34 | Integer i = foo2.getFoo(); | 
                  
                          |   | 35 | }}} | 
                  
                          |   | 36 |  | 
                  
                          |   | 37 | 回過頭來看看下面的宣告: | 
                  
                          |   | 38 | {{{ | 
                  
                          |   | 39 | GenericFoo<Boolean> foo1 = new GenericFoo<Boolean>(); | 
                  
                          |   | 40 | GenericFoo<Integer> foo2 = new GenericFoo<Integer>(); | 
                  
                          |   | 41 |  }}} | 
                  
                          |   | 42 |  | 
                  
                          |   | 43 | GenericFoo< Boolean>宣告的foo1與GenericFoo< Integer>宣告的foo2是相同的類型嗎?答案是否定的,基本上它們分屬於兩個不同類別的類型,即「相當於」下面兩個類型(只是個比喻): | 
                  
                          |   | 44 | {{{ | 
                  
                          |   | 45 | public class GenericFooBoolean { | 
                  
                          |   | 46 |     private Boolean foo; | 
                  
                          |   | 47 |   | 
                  
                          |   | 48 |     public void setFoo(Boolean foo) { | 
                  
                          |   | 49 |         this.foo = foo; | 
                  
                          |   | 50 |     } | 
                  
                          |   | 51 |   | 
                  
                          |   | 52 |     public Boolean getFoo() { | 
                  
                          |   | 53 |         return foo; | 
                  
                          |   | 54 |     } | 
                  
                          |   | 55 | } | 
                  
                          |   | 56 | }}} | 
                  
                          |   | 57 | 以及: | 
                  
                          |   | 58 | {{{ | 
                  
                          |   | 59 | public class GenericFooInteger { | 
                  
                          |   | 60 |     private Integer foo; | 
                  
                          |   | 61 |   | 
                  
                          |   | 62 |     public void setFoo(Integer foo) { | 
                  
                          |   | 63 |         this.foo = foo; | 
                  
                          |   | 64 |     } | 
                  
                          |   | 65 |   | 
                  
                          |   | 66 |     public Integer getFoo() { | 
                  
                          |   | 67 |         return foo; | 
                  
                          |   | 68 |     } | 
                  
                          |   | 69 | } | 
                  
                          |   | 70 | }}}  | 
                  
                          |   | 71 |  | 
                  
                          |   | 72 | 所以您不可以將 foo1 指定給 foo2,或是將 foo2 指定給 foo1,編譯器會回報錯誤。[[BR]] | 
                  
                          |   | 73 | [[BR]] | 
                  
                          |   | 74 |  | 
                  
                          |   | 75 |  = 幾個定義泛型的例子 =  | 
                  
                          |   | 76 | 您可以在定義泛型類別時,宣告多個類型持有者,例如: | 
                  
                          |   | 77 |  | 
                  
                          |   | 78 |     * GenericFoo.java | 
                  
                          |   | 79 |  | 
                  
                          |   | 80 | {{{ | 
                  
                          |   | 81 | public class GenericFoo<T1, T2> { | 
                  
                          |   | 82 |     private T1 foo1; | 
                  
                          |   | 83 |     private T2 foo2; | 
                  
                          |   | 84 |   | 
                  
                          |   | 85 |     public void setFoo1(T1 foo1) { | 
                  
                          |   | 86 |         this.foo1 = foo1; | 
                  
                          |   | 87 |     } | 
                  
                          |   | 88 |   | 
                  
                          |   | 89 |     public T1 getFoo1() { | 
                  
                          |   | 90 |         return foo1; | 
                  
                          |   | 91 |     } | 
                  
                          |   | 92 |   | 
                  
                          |   | 93 |     public void setFoo2(T2 foo2) { | 
                  
                          |   | 94 |         this.foo2 = foo2; | 
                  
                          |   | 95 |     } | 
                  
                          |   | 96 |   | 
                  
                          |   | 97 |     public T2 getFoo2() { | 
                  
                          |   | 98 |         return foo2; | 
                  
                          |   | 99 |     } | 
                  
                          |   | 100 | }  | 
                  
                          |   | 101 | }}} | 
                  
                          |   | 102 | 您可以如下使用GenericFoo類別,分別以Integer與Boolean取代T1與T2:[[BR]] | 
                  
                          |   | 103 |  | 
                  
                          |   | 104 |  | 
                  
                          |   | 105 | GenericFoo<Integer, Boolean> foo = new GenericFoo<Integer, Boolean>();[[BR]] | 
                  
                          |   | 106 |  | 
                  
                          |   | 107 |  | 
                  
                          |   | 108 | 如果是陣列的話,可以像這樣: | 
                  
                          |   | 109 |  | 
                  
                          |   | 110 |     * GenericFoo.java | 
                  
                          |   | 111 |  | 
                  
                          |   | 112 | {{{ | 
                  
                          |   | 113 | public class GenericFoo<T> { | 
                  
                          |   | 114 |     private T[] fooArray; | 
                  
                          |   | 115 |  | 
                  
                          |   | 116 |     public void setFooArray(T[] fooArray) { | 
                  
                          |   | 117 |         this.fooArray = fooArray; | 
                  
                          |   | 118 |     } | 
                  
                          |   | 119 |  | 
                  
                          |   | 120 |     public T[] getFooArray() { | 
                  
                          |   | 121 |         return fooArray; | 
                  
                          |   | 122 |     } | 
                  
                          |   | 123 | }  | 
                  
                          |   | 124 | }}} | 
                  
                          |   | 125 |  | 
                  
                          |   | 126 | 您可以像下面的方式來使用它: | 
                  
                          |   | 127 | {{{ | 
                  
                          |   | 128 | String[] strs = {"caterpillar", "momor", "bush"}; | 
                  
                          |   | 129 |   | 
                  
                          |   | 130 | GenericFoo<String> foo = new GenericFoo<String>(); | 
                  
                          |   | 131 | foo.setFooArray(strs); | 
                  
                          |   | 132 | strs = foo.getFooArray(); | 
                  
                          |   | 133 | }}} | 
                  
                          |   | 134 | 來改寫一下 Object 類別 中的 SimpleCollection: | 
                  
                          |   | 135 |  | 
                  
                          |   | 136 |     * SimpleCollection.java | 
                  
                          |   | 137 |  | 
                  
                          |   | 138 | {{{ | 
                  
                          |   | 139 | public class SimpleCollection<T> {  | 
                  
                          |   | 140 |     private T[] objArr;  | 
                  
                          |   | 141 |     private int index = 0;  | 
                  
                          |   | 142 |   | 
                  
                          |   | 143 |     public SimpleCollection() {  | 
                  
                          |   | 144 |         objArr = (T[]) new Object[10]; // 預設10個物件空間  | 
                  
                          |   | 145 |     }  | 
                  
                          |   | 146 |   | 
                  
                          |   | 147 |     public SimpleCollection(int capacity) {  | 
                  
                          |   | 148 |         objArr = (T[]) new Object[capacity];  | 
                  
                          |   | 149 |     }  | 
                  
                          |   | 150 |   | 
                  
                          |   | 151 |     public void add(T t) {  | 
                  
                          |   | 152 |         objArr[index] = t;  | 
                  
                          |   | 153 |         index++;  | 
                  
                          |   | 154 |     }  | 
                  
                          |   | 155 |   | 
                  
                          |   | 156 |     public int getLength() {  | 
                  
                          |   | 157 |         return index;  | 
                  
                          |   | 158 |     }  | 
                  
                          |   | 159 |   | 
                  
                          |   | 160 |     public T get(int i) {  | 
                  
                          |   | 161 |         return (T) objArr[i];  | 
                  
                          |   | 162 |     }  | 
                  
                          |   | 163 | }  | 
                  
                          |   | 164 | }}} | 
                  
                          |   | 165 |  | 
                  
                          |   | 166 | 現在您可以直接使用它來當作特定類型物件的容器,例如: | 
                  
                          |   | 167 |  | 
                  
                          |   | 168 |     * Test.java | 
                  
                          |   | 169 | {{{ | 
                  
                          |   | 170 | public class Test {  | 
                  
                          |   | 171 |     public static void main(String[] args) {  | 
                  
                          |   | 172 |         SimpleCollection<Integer> c =  | 
                  
                          |   | 173 |                 new SimpleCollection<Integer>();  | 
                  
                          |   | 174 |   | 
                  
                          |   | 175 |         for(int i = 0; i < 10; i++) { | 
                  
                          |   | 176 |             c.add(new Integer(i)); | 
                  
                          |   | 177 |         } | 
                  
                          |   | 178 |  | 
                  
                          |   | 179 |         for(int i = 0; i < 10; i++) { | 
                  
                          |   | 180 |             Integer k = c.get(i); | 
                  
                          |   | 181 |         } | 
                  
                          |   | 182 |     }  | 
                  
                          |   | 183 | }  | 
                  
                          |   | 184 | }}} | 
                  
                          |   | 185 |  | 
                  
                          |   | 186 | 另一個SimpleCollection的寫法也可以如下,作用是一樣的: | 
                  
                          |   | 187 |  | 
                  
                          |   | 188 |     * SimpleCollection.java | 
                  
                          |   | 189 | {{{ | 
                  
                          |   | 190 | public class SimpleCollection<T> {  | 
                  
                          |   | 191 |     private Object[] objArr;  | 
                  
                          |   | 192 |     private int index = 0;  | 
                  
                          |   | 193 |   | 
                  
                          |   | 194 |     public SimpleCollection() {  | 
                  
                          |   | 195 |         objArr = new Object[10]; // 預設10個物件空間  | 
                  
                          |   | 196 |     }  | 
                  
                          |   | 197 |   | 
                  
                          |   | 198 |     public SimpleCollection(int capacity) {  | 
                  
                          |   | 199 |         objArr = new Object[capacity];  | 
                  
                          |   | 200 |     }  | 
                  
                          |   | 201 |   | 
                  
                          |   | 202 |     public void add(T t) {  | 
                  
                          |   | 203 |         objArr[index] = t;  | 
                  
                          |   | 204 |         index++;  | 
                  
                          |   | 205 |     }  | 
                  
                          |   | 206 |   | 
                  
                          |   | 207 |     public int getLength() {  | 
                  
                          |   | 208 |         return index;  | 
                  
                          |   | 209 |     }  | 
                  
                          |   | 210 |   | 
                  
                          |   | 211 |     public T get(int i) {  | 
                  
                          |   | 212 |         return (T) objArr[i];  | 
                  
                          |   | 213 |     }  | 
                  
                          |   | 214 | }  | 
                  
                          |   | 215 | }}} | 
                  
                          |   | 216 |  | 
                  
                          |   | 217 | 如果您已經定義了一個泛型類別,想要用這個類別來於另一個泛型類別中宣告成員的話要如何作?舉個實例,假設您已經定義了下面的類別: | 
                  
                          |   | 218 |  | 
                  
                          |   | 219 |     * GenericFoo.java | 
                  
                          |   | 220 | {{{ | 
                  
                          |   | 221 | public class GenericFoo<T> { | 
                  
                          |   | 222 |     private T foo; | 
                  
                          |   | 223 |   | 
                  
                          |   | 224 |     public void setFoo(T foo) { | 
                  
                          |   | 225 |         this.foo = foo; | 
                  
                          |   | 226 |     } | 
                  
                          |   | 227 |   | 
                  
                          |   | 228 |     public T getFoo() { | 
                  
                          |   | 229 |         return foo; | 
                  
                          |   | 230 |     } | 
                  
                          |   | 231 | }  | 
                  
                          |   | 232 | }}} | 
                  
                          |   | 233 |  | 
                  
                          |   | 234 | 您想要寫一個包裝類別(Wrapper),這個類別必須也具有GenericFoo的泛型功能,您可以這麼寫: | 
                  
                          |   | 235 |  | 
                  
                          |   | 236 |     * WrapperFoo.java | 
                  
                          |   | 237 | {{{ | 
                  
                          |   | 238 | public class WrapperFoo<T> { | 
                  
                          |   | 239 |     private GenericFoo<T> foo; | 
                  
                          |   | 240 |      | 
                  
                          |   | 241 |     public void setFoo(GenericFoo<T> foo) { | 
                  
                          |   | 242 |         this.foo = foo; | 
                  
                          |   | 243 |     } | 
                  
                          |   | 244 |   | 
                  
                          |   | 245 |     public GenericFoo<T> getFoo() { | 
                  
                          |   | 246 |         return foo; | 
                  
                          |   | 247 |     } | 
                  
                          |   | 248 | }  | 
                  
                          |   | 249 | }}} | 
                  
                          |   | 250 |  | 
                  
                          |   | 251 |  | 
                  
                          |   | 252 | 這麼一來,您就可以保留型態持有者 T 的功能,一個使用的例子如下: | 
                  
                          |   | 253 | {{{ | 
                  
                          |   | 254 | GenericFoo<Integer> foo = new GenericFoo<Integer>(); | 
                  
                          |   | 255 | foo.setFoo(new Integer(10)); | 
                  
                          |   | 256 |   | 
                  
                          |   | 257 | WrapperFoo<Integer> wrapper = new WrapperFoo<Integer>(); | 
                  
                          |   | 258 | wrapper.setFoo(foo); | 
                  
                          |   | 259 | }}} | 
                  
                          |   | 260 |  | 
                  
                          |   | 261 |  = 限制泛型可用類型 =  | 
                  
                          |   | 262 | 在定義泛型類別時,預設您可以使用任何的型態來實例化泛型類別中的型態持有者,但假設您想要限制使用泛型類別時,只能用某個特定型態或其子類別才能實例化型態持有者的話呢?[[BR]] | 
                  
                          |   | 263 |  | 
                  
                          |   | 264 | 您可以在定義型態持有者時,一併使用"extends"指定這個型態持有者必須是擴充某個類型,舉個實例來說: | 
                  
                          |   | 265 |  | 
                  
                          |   | 266 |     * ListGenericFoo.java | 
                  
                          |   | 267 |  | 
                  
                          |   | 268 | {{{ | 
                  
                          |   | 269 | import java.util.List; | 
                  
                          |   | 270 |  | 
                  
                          |   | 271 | public class ListGenericFoo<T extends List> { | 
                  
                          |   | 272 |     private T[] fooArray; | 
                  
                          |   | 273 |  | 
                  
                          |   | 274 |     public void setFooArray(T[] fooArray) { | 
                  
                          |   | 275 |         this.fooArray = fooArray; | 
                  
                          |   | 276 |     } | 
                  
                          |   | 277 |  | 
                  
                          |   | 278 |     public T[] getFooArray() { | 
                  
                          |   | 279 |         return fooArray; | 
                  
                          |   | 280 |     } | 
                  
                          |   | 281 | }  | 
                  
                          |   | 282 | }}} | 
                  
                          |   | 283 |  | 
                  
                          |   | 284 | ListGenericFoo在宣告類型持有者時,一併指定這個持有者必須擴充自List介面(interface),在限定持有者時,無論是要限定的對象是介面或類別,都是使用"extends"關鍵字。[[BR]] | 
                  
                          |   | 285 | [[BR]] | 
                  
                          |   | 286 |  | 
                  
                          |   | 287 |  | 
                  
                          |   | 288 | 您使用"extends"限定型態持有者必須是實作List的類別或其子類別,例如LinkedList與ArrayList,下面的程式是合法的:[[BR]] | 
                  
                          |   | 289 | {{{ | 
                  
                          |   | 290 | ListGenericFoo<LinkedList> foo1 = | 
                  
                          |   | 291 |                   new ListGenericFoo<LinkedList>(); | 
                  
                          |   | 292 | ListGenericFoo<ArrayList> foo2 = | 
                  
                          |   | 293 |                   new ListGenericFoo<ArrayList>(); | 
                  
                          |   | 294 | }}} | 
                  
                          |   | 295 |  | 
                  
                          |   | 296 | 但是如果不是List的類別或是其子類別,就會發生編譯錯誤,例如下面的程式通不過編譯:[[BR]] | 
                  
                          |   | 297 | [[BR]] | 
                  
                          |   | 298 |  | 
                  
                          |   | 299 | '''ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>();'''[[BR]] | 
                  
                          |   | 300 | [[BR]] | 
                  
                          |   | 301 |  | 
                  
                          |   | 302 | 編譯器會回報以下錯誤訊息:[[BR]] | 
                  
                          |   | 303 | type parameter java.util.HashMap is not within its bound[[BR]] | 
                  
                          |   | 304 | ListGenericFoo<HashMap> foo3 = new ListGenericFoo<HashMap>();[[BR]][[BR]] | 
                  
                          |   | 305 |  | 
                  
                          |   | 306 | HashMap並沒有實作List介面,所以無法用來實例化型態持有者,事實上,當您沒有使用extends關鍵字限定型態持有者時,預設則是Object下的所有子類別都可以實例化型態持有者,即只寫<T>時就相當於<T extends Object>。[[BR]] | 
                  
                          |   | 307 |  | 
                  
                          |   | 308 |  = 型態通配字元 =  | 
                  
                          |   | 309 |  | 
                  
                          |   | 310 | 假設您撰寫了一個泛型類別: | 
                  
                          |   | 311 |  | 
                  
                          |   | 312 |     * GenericFoo.java | 
                  
                          |   | 313 | {{{ | 
                  
                          |   | 314 | public class GenericFoo<T> { | 
                  
                          |   | 315 |     private T foo; | 
                  
                          |   | 316 |   | 
                  
                          |   | 317 |     public void setFoo(T foo) { | 
                  
                          |   | 318 |         this.foo = foo; | 
                  
                          |   | 319 |     } | 
                  
                          |   | 320 |   | 
                  
                          |   | 321 |     public T getFoo() { | 
                  
                          |   | 322 |         return foo; | 
                  
                          |   | 323 |     } | 
                  
                          |   | 324 | } | 
                  
                          |   | 325 | }}} | 
                  
                          |   | 326 |  | 
                  
                          |   | 327 | 分別使用下面的程式宣告了foo1與foo2兩個參考名稱:[[BR]] | 
                  
                          |   | 328 | [[BR]] | 
                  
                          |   | 329 |  | 
                  
                          |   | 330 | '''GenericFoo<Integer> foo1 = null;[[BR]] | 
                  
                          |   | 331 |  | 
                  
                          |   | 332 | GenericFoo<Boolean> foo2 = null;'''[[BR]] | 
                  
                          |   | 333 |  | 
                  
                          |   | 334 |   | 
                  
                          |   | 335 |  | 
                  
                          |   | 336 | 那麼 foo1 就只接受GenericFoo<Integer>的實例,而foo2只接受GenericFoo<Boolean>的實例。[[BR]] | 
                  
                          |   | 337 | [[BR]] | 
                  
                          |   | 338 |  | 
                  
                          |   | 339 | 現在您有這麼一個需求,您希望有一個參考名稱foo可以接受所有下面的實例(List、Map或List介面以及其實介面的相關類別,在J2SE 5.0中已經針對泛型功能作了改寫,在這邊仍請將之當作介面就好,這是為了簡化說明的考量):[[BR]][[BR]] | 
                  
                          |   | 340 |  | 
                  
                          |   | 341 | '''foo = new GenericFoo<ArrayList>(); | 
                  
                          |   | 342 | foo = new GenericFoo<LinkedList>();'''[[BR]][[BR]] | 
                  
                          |   | 343 |  | 
                  
                          |   | 344 | 簡單的說,實例化型態持有者時,它必須是實作List的類別或其子類別,要宣告這麼一個參考名稱,您可以使用 '?' 通配字元,並使用"extends"關鍵字限定型態持有者的型態,例如[[BR]] | 
                  
                          |   | 345 | [[BR]] | 
                  
                          |   | 346 |  | 
                  
                          |   | 347 | '''GenericFoo<? extends List> foo = null; | 
                  
                          |   | 348 | foo = new GenericFoo<ArrayList>(); | 
                  
                          |   | 349 | ..... | 
                  
                          |   | 350 | foo = new GenericFoo<LinkedList>(); | 
                  
                          |   | 351 | ....'''[[BR]] | 
                  
                          |   | 352 | [[BR]] | 
                  
                          |   | 353 |  | 
                  
                          |   | 354 |   | 
                  
                          |   | 355 |  | 
                  
                          |   | 356 | 如果指定了不是實作List的類別或其子類別,則編譯器會回報錯誤,例如:[[BR]] | 
                  
                          |   | 357 | [[BR]] | 
                  
                          |   | 358 |  | 
                  
                          |   | 359 | '''GenericFoo<? extends List> foo = new GenericFoo<HashMap>();'''[[BR]] | 
                  
                          |   | 360 |  | 
                  
                          |   | 361 | 上面這段程式編譯器會回報以下的錯誤: | 
                  
                          |   | 362 | {{{ | 
                  
                          |   | 363 | incompatible types | 
                  
                          |   | 364 | found : GenericFoo<java.util.HashMap> | 
                  
                          |   | 365 | required: GenericFoo<? extends java.util.List> | 
                  
                          |   | 366 | GenericFoo<? extends List> foo = new GenericFoo<HashMap>(); | 
                  
                          |   | 367 | }}} | 
                  
                          |   | 368 |  | 
                  
                          |   | 369 | 這樣的限定是很有用的,例如如果您想要自訂一個showFoo()方法,方法的內容實作是針對List而制定的,例如:[[BR]] | 
                  
                          |   | 370 | [[BR]] | 
                  
                          |   | 371 |  | 
                  
                          |   | 372 | public void showFoo(GenericFoo foo) {[[BR]] | 
                  
                          |   | 373 |      // 針對List而制定的內容[[BR]] | 
                  
                          |   | 374 | }[[BR]] | 
                  
                          |   | 375 | [[BR]] | 
                  
                          |   | 376 |  | 
                  
                          |   | 377 | 您當然不希望任何的型態都可以傳入showFoo()方法中,您可以使用以下的方式來限定,例如: | 
                  
                          |   | 378 | '''public void showFoo(GenericFoo<? extends List> foo) {[[BR]] | 
                  
                          |   | 379 |  | 
                  
                          |   | 380 | }'''[[BR]] | 
                  
                          |   | 381 | [[BR]] | 
                  
                          |   | 382 |  | 
                  
                          |   | 383 |   | 
                  
                          |   | 384 | 這麼一來,如果有粗心的程式設計人員傳入了您不想要的型態,例如GenericFoo<Boolean>型態的實例,則編譯器都會告訴它這是不可行的,在宣告名稱時如果指定了<?>而不使用"extends",則預設是允許Object及其下的子類,也就是所有的Java物件了,那為什麼不直接使用GenericFoo宣告就好了,何必要用GenericFoo<?>來宣告?使用通配字元有點要注意的是,透過使用通配字元宣告的名稱所參考的物件,您沒辦法再對它加入新的資訊,您只能取得它的資訊或是移除它的資訊,例如: | 
                  
                          |   | 385 | {{{ | 
                  
                          |   | 386 | GenericFoo<String> foo = new GenericFoo<String>(); | 
                  
                          |   | 387 | foo.setFoo("caterpillar"); | 
                  
                          |   | 388 | GenericFoo<?> immutableFoo = foo; | 
                  
                          |   | 389 |  | 
                  
                          |   | 390 | // 可以取得資訊 | 
                  
                          |   | 391 | System.out.println(immutableFoo.getFoo()); | 
                  
                          |   | 392 |  | 
                  
                          |   | 393 | // 可透過immutableFoo來移去foo所參考實例內的資訊 | 
                  
                          |   | 394 | immutableFoo.setFoo(null); | 
                  
                          |   | 395 |  | 
                  
                          |   | 396 | // 不可透過immutableFoo來設定新的資訊給foo所參考的實例 | 
                  
                          |   | 397 | // 所以下面這行無法通過編譯 | 
                  
                          |   | 398 | //  immutableFoo.setFoo("良葛格"); | 
                  
                          |   | 399 | }}} | 
                  
                          |   | 400 | 所以使用<?>或是<? extends SomeClass>的宣告方式,意味著您只能透過該名稱來取得所參考實例的資訊,或者是移除某些資訊,但不能增加它的資訊,因為只知道當中放置的是SomeClass的子類,但不確定是什麼類的實例,編譯器不讓您加入物件,理由是,如果可以加入物件的話,那麼您就得記得取回的物件實例是什麼形態,然後轉換為原來的型態方可進行操作,這就失去了使用泛型的意義。[[BR]] | 
                  
                          |   | 401 | [[BR]] | 
                  
                          |   | 402 |  | 
                  
                          |   | 403 | 事實上,GenericFoo<?> immutableFoo相當於GenericFoo immutableFoo。[[BR]] | 
                  
                          |   | 404 | [[BR]] | 
                  
                          |   | 405 |  | 
                  
                          |   | 406 |  | 
                  
                          |   | 407 |  | 
                  
                          |   | 408 | 除了可以向下限制,您也可以向上限制,只要使用"super"關鍵字,例如:[[BR]] | 
                  
                          |   | 409 | [[BR]] | 
                  
                          |   | 410 |  | 
                  
                          |   | 411 | '''GenericFoo<? super StringBuilder> foo;'''[[BR]] | 
                  
                          |   | 412 |  | 
                  
                          |   | 413 |   | 
                  
                          |   | 414 | 如此,foo就只接受 StringBuilder 及其上層的父類型態之物件。[[BR]] | 
                  
                          |   | 415 |  | 
                  
                          |   | 416 |  = 擴充泛型類別、實作泛型介面 =  | 
                  
                          |   | 417 | 您可以擴充一個泛型類別,保留其型態持有者,並新增自己的型態持有者,例如先寫一個父類別: | 
                  
                          |   | 418 |  | 
                  
                          |   | 419 |     * GenericFoo.java | 
                  
                          |   | 420 | {{{ | 
                  
                          |   | 421 | public class GenericFoo<T1, T2> { | 
                  
                          |   | 422 |     private T1 foo1; | 
                  
                          |   | 423 |     private T2 foo2; | 
                  
                          |   | 424 |   | 
                  
                          |   | 425 |     public void setFoo1(T1 foo1) { | 
                  
                          |   | 426 |         this.foo1 = foo1; | 
                  
                          |   | 427 |     } | 
                  
                          |   | 428 |   | 
                  
                          |   | 429 |     public T1 getFoo1() { | 
                  
                          |   | 430 |         return foo1; | 
                  
                          |   | 431 |     } | 
                  
                          |   | 432 |   | 
                  
                          |   | 433 |     public void setFoo2(T2 foo2) { | 
                  
                          |   | 434 |         this.foo2 = foo2; | 
                  
                          |   | 435 |     } | 
                  
                          |   | 436 |   | 
                  
                          |   | 437 |     public T2 getFoo2() { | 
                  
                          |   | 438 |         return foo2; | 
                  
                          |   | 439 |     } | 
                  
                          |   | 440 | }  | 
                  
                          |   | 441 | }}} | 
                  
                          |   | 442 |  | 
                  
                          |   | 443 |  | 
                  
                          |   | 444 | 再來寫一個子類別擴充上面的父類別: | 
                  
                          |   | 445 | {{{ | 
                  
                          |   | 446 |     * SubGenericFoo.java | 
                  
                          |   | 447 |  | 
                  
                          |   | 448 | public class SubGenericFoo<T1, T2, T3>  | 
                  
                          |   | 449 |                                extends GenericFoo<T1, T2> { | 
                  
                          |   | 450 |     private T3 foo3; | 
                  
                          |   | 451 |    | 
                  
                          |   | 452 |     public void setFoo3(T3 foo3) { | 
                  
                          |   | 453 |         this.foo3 = foo3; | 
                  
                          |   | 454 |     } | 
                  
                          |   | 455 |   | 
                  
                          |   | 456 |     public T3 getFoo3() { | 
                  
                          |   | 457 |         return foo3; | 
                  
                          |   | 458 |     } | 
                  
                          |   | 459 | }  | 
                  
                          |   | 460 | }}} | 
                  
                          |   | 461 | 如果決定要保留型態持有者,則父類別上宣告的型態持有者數目必須齊全,也就是說上式中,T1與T2都要出現,如果不保留型態持有者,則繼承下來的T1與 T2自動變為Object,建議當然是父類別的型態持有者都保留。 | 
                  
                          |   | 462 |  | 
                  
                          |   | 463 | 介面實作也是類似,例如先定義一個介面: | 
                  
                          |   | 464 |  | 
                  
                          |   | 465 |     * IFoo.java | 
                  
                          |   | 466 | {{{ | 
                  
                          |   | 467 | public interface IFoo<T1, T2> { | 
                  
                          |   | 468 |     public void setFoo1(T1 foo1); | 
                  
                          |   | 469 |     public void setFoo2(T2 foo2); | 
                  
                          |   | 470 |     public T1 getFoo1(); | 
                  
                          |   | 471 |     public T2 getFoo2(); | 
                  
                          |   | 472 | } | 
                  
                          |   | 473 | }}} | 
                  
                          |   | 474 |  | 
                  
                          |   | 475 | 實作時如下,保留所有的型態持有者: | 
                  
                          |   | 476 |  | 
                  
                          |   | 477 |     * GenericFoo.java | 
                  
                          |   | 478 | {{{ | 
                  
                          |   | 479 | public class GenericFoo<T1, T2> implements IFoo<T1, T2> { | 
                  
                          |   | 480 |     private T1 foo1; | 
                  
                          |   | 481 |     private T2 foo2; | 
                  
                          |   | 482 |   | 
                  
                          |   | 483 |     public void setFoo1(T1 foo1) { | 
                  
                          |   | 484 |         this.foo1 = foo1; | 
                  
                          |   | 485 |     } | 
                  
                          |   | 486 |   | 
                  
                          |   | 487 |     public T1 getFoo1() { | 
                  
                          |   | 488 |         return foo1; | 
                  
                          |   | 489 |     } | 
                  
                          |   | 490 |   | 
                  
                          |   | 491 |     public void setFoo2(T2 foo2) { | 
                  
                          |   | 492 |         this.foo2 = foo2; | 
                  
                          |   | 493 |     } | 
                  
                          |   | 494 |   | 
                  
                          |   | 495 |     public T2 getFoo2() { | 
                  
                          |   | 496 |         return foo2; | 
                  
                          |   | 497 |     } | 
                  
                          |   | 498 | } | 
                  
                          |   | 499 | }}} |