程式碼及說明:
1 package com.zsm.crazyjava; 2 3 import java.util.ArrayList; 4 import java.util.Collection; 5 import java.util.List; 6 import java.util.Set; 7 8 /** 9 * @author zsm 10 * @date 2016年11月2日 下午11:23:30 11 * @version 1.0 12 * @parameter 13 * @return 14 */ 15 // JDK5開始支援泛型。泛型(類、介面、方法);泛型萬用字元、萬用字元上限、萬用字元下限 16 public class _16_Generic { 17 // 泛型 18 List<String> strListStand = new ArrayList<String>(); 19 // 泛型菱形語法(JDK7後初始化時泛型的具體型別可省略,自動判斷) 20 List<String> strList = new ArrayList<>(); 21 22 // 泛型類、介面 23 public interface Fruit<T> { 24 void add(T fruit); 25 } 26 27 public interface MyMap<K, V> { 28 Set<K> keySet(); 29 30 V put(K key, V value); 31 } 32 33 public static class Apple<T> { 34 private T info; 35 36 public Apple() {// 不能再寫成Apple<T> 37 38 } 39 40 public Apple(T info) { 41 this.info = info; 42 } 43 44 public T getInfo() { 45 return this.info; 46 } 47 48 // 靜態方法、靜態初始化塊、靜態變數宣告和初始化中不能使用泛型形參,靜態類或介面定義處則可以 49 // static T name;//報錯 50 // public static void bar(T msg) {} 51 // static {T name = "xiaoming";} 52 53 // 泛型方法則可以使用型別形參,即使是靜態方法。型別形參放在方法修飾符和返回值之間 54 public static <E, F> void ttt(E name, F age) { 55 ; 56 } 57 } 58 59 // 泛型派生的子類、實現泛型介面的類的宣告中不能再包含泛型形參 60 public class A1 extends Apple<String> {// 不能再寫成Apple<T> 61 public String getInfo() { 62 return super.info; 63 } 64 } 65 66 // 帶泛型形參的類不會隨著型別實參的不同而成為不同的類,介面亦然 67 static void testClassEqual() { 68 System.out.println((new Apple<String>()).getClass() == (new Apple<Integer>()).getClass());// true 69 List<String> strList = new ArrayList<>(); 70 List<Integer> intList = new ArrayList<>(); 71 System.out.println(strList.getClass() == intList.getClass());// true 72 } 73 74 // 若Foo是Bar的子類,則Foo[]是Bar[]的子類,但List<Foo>卻不是List<Bar>的子類。可以使用泛型萬用字元:?, 75 public void wildcardTest1(List<Object> c) {// 呼叫時只能傳入List<Object>引數,不可傳入List<String>等引數,因為後者不是前者的子類 76 for (int i = 0; i < c.size(); i++) { 77 System.out.println(c.get(i)); 78 } 79 } 80 81 public void wildcardTest2(List<?> c) {// 此時可傳入List<String>等引數,但只可從c讀不可往裡寫,因為c型別不定。即帶型別萬用字元定義的物件只可讀不可寫 82 for (int i = 0; i < c.size(); i++) { 83 System.out.println(c.get(i)); 84 // c.add(new Object());//編譯錯誤,不可往c裡寫,因為c裡元素型別未定 85 } 86 } 87 88 // 型別萬用字元上限 89 public abstract class Shape { 90 public abstract void draw(MyCanvas c); 91 } 92 93 public class Circle extends Shape { 94 @Override 95 public void draw(MyCanvas c) { 96 // TODO Auto-generated method stub 97 System.out.println("draw a circle on " + c); 98 } 99 } 100 101 public class Rectangle extends Shape { 102 @Override 103 public void draw(MyCanvas c) { 104 // TODO Auto-generated method stub 105 System.out.println("draw a rectangle on " + c); 106 } 107 } 108 109 public class MyCanvas { 110 public void drawAll0(List<?> shapes) {// 使用萬用字元時型別未定,所以訪問每個元素只能用終極父類Object,這導致得進行麻煩的強制型別轉換 111 for (Object obj : shapes) { 112 Shape s = (Shape) obj; 113 s.draw(this); 114 } 115 } 116 117 public void drawAll1(List<? extends Shape> shapes) {// 使用萬用字元上限,從而知道元素元素型別的終極父類是Shape,這樣不用進行強轉。至多可以有一個類上限、多個介面上限,類上限需放最前。 118 for (Shape s : shapes) { 119 s.draw(this); 120 } 121 } 122 } 123 124 // 泛型方法:方法中所用型別形參不要求在類宣告中先出現該型別形參;型別形參位於方法修飾符和返回值之間;與泛型類、介面不同的是,無須在呼叫泛型方法時顯式指明實參型別 125 public <T> void fromArrayToCollection(T[] a, Collection<T> c) {// a的實參可以是T[]的子型別 126 for (T o : a) { 127 c.add(o); 128 } 129 } 130 131 public void test_fromArrayToCollection() { 132 fromArrayToCollection((new Object[10]), new ArrayList<Object>()); 133 fromArrayToCollection((new Integer[10]), new ArrayList<Object>()); 134 fromArrayToCollection((new Integer[10]), new ArrayList<>());// 與泛型類、介面不同的是,無須在呼叫泛型方法前顯式指明實參型別(指在方法名前),編譯器自己確定 135 fromArrayToCollection((new Integer[10]), new ArrayList<Number>()); 136 // fromArrayToCollection((new Integer[10]), new ArrayList<String>());//編譯錯誤 137 } 138 139 // 泛型方法和型別萬用字元:方法引數間或返回值與引數間存在型別依賴關係(如子類)時採用泛型方法,否則型別引數只用一次,沒有存在的必要,可以改用型別萬用字元。 140 public <T, S extends T> void copy1(List<T> des, List<S> src) {// S僅用了一次且與其他引數間沒有依賴關係,因此沒有存在的必要,改為下面的方法。 141 for (S s : src) { 142 des.add(s); 143 } 144 } 145 146 public <T> void copy2(List<T> des, List<? extends T> src) {// 去掉S,改為使用型別萬用字元上限。 147 for (T t : src) { 148 des.add(t); 149 } 150 } 151 152 // 泛型方法 ———— 泛型構造器 153 class Foo<E> { 154 public <T> Foo(T t) { 155 System.out.println(t); 156 } 157 158 public void add(E e) { 159 160 } 161 } 162 163 public void test_genericConstructor() { 164 new Foo("good"); 165 new Foo(1); 166 new<String> Foo("good");// 顯示指定構造方法型別形參的實際型別 167 new<String> Foo<Integer>("good");// 又指定了類的型別形參的實際型別 168 new Foo<>("good");// 菱形語法 169 // new<String> Foo<>(1);// 如果顯示指定了構造器型別形參的型別,則不可用菱形語法 170 171 Foo<Integer> p1 = new<String> Foo("good"); 172 Foo<Integer> p2 = new<String> Foo<Integer>("good"); 173 Foo<Integer> p3 = new Foo<Integer>("good"); 174 // Foo<Integer> p4 = new<String> Foo<>("good");//如果顯示指定了構造器型別形參的型別,則不可用菱形語法 175 } 176 177 // 型別萬用字元下限。 178 // 返回最後一個元素,型別不可丟。上面的copy2方法如果要返回最後一個被複制的元素,則返回的會是des中元素的型別T,這樣就丟失了src的型別即S。可以通過修改copy1解決,也可以通過萬用字元下限解決。 179 public <T, S extends T> S copy3(List<T> des, List<S> src) {// S僅用了一次且與其他引數間沒有依賴關係,因此沒有存在的必要,改為下面的方法。 180 int i = 0; 181 for (i = 0; i < src.size(); i++) { 182 des.add(src.get(i)); 183 } 184 return src.get(i - 1); 185 } 186 187 public <T> T copy4(List<? super T> des, List<T> src) {// 去掉S,改為型別萬用字元下限。 188 int i = 0; 189 for (i = 0; i < src.size(); i++) { 190 des.add(src.get(i)); 191 } 192 return src.get(i - 1); 193 } 194 195 // 檫除與轉換:帶泛型宣告的類間轉換 196 public static void test_Transform() { 197 List list;// 不指定實際型別引數,為raw type,預設為上限型別,此為Object 198 List<Integer> listInteger = new ArrayList<Integer>(); 199 listInteger.add(1); 200 listInteger.add(2); 201 list = listInteger;// List<Integer>物件賦給未指定型別的List,原始型別丟失,變為Object 202 203 List<String> listStr = list;// 編譯沒問題,只有警告:"未經檢查的轉換" 204 System.out.println(listStr.get(0));// 但訪問裡面元素,會發生執行時異常 205 } 206 207 public static void main(String[] args) { 208 // TODO Auto-generated method stub 209 210 } 211 212 }