Java泛型及實踐

weixin_34120274發表於2019-01-11

程式碼及說明:

  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 }
View Code

 

相關文章