java8泛型
- 2019 年 10 月 3 日
- 筆記
泛型,也就是將類型參數化,然後在使用類或者方法的時候可以傳入類型,在不需要創建新類型實現的前提下可以通過泛型控制形參的類型。泛型可以用在類,方法和接口中。
1,泛型中的相關操作符
在使用泛型的代碼中經常可以看到一些泛型相關的符號,它們的作用如下表所示:
類型 | 功能 |
---|---|
T,E,K,V | 泛型標識,可以寫人任意標識,不同字母更多是一種約定,等同於< T extends Object> |
? | 無限制通配符,表示不確定類型,等同於< ? extends Object> |
extend | 上界通配符 |
super | 下界通配符 |
& | 附加約束(AdditionalBound, tjls8-4.4) |
2,泛型基本使用示例
public class GenericDemo<C> { //泛型類 public static void main(String[] args) { //泛型類用Integer初始化,所以C相關的方法屬性必須是Integer GenericDemo<Integer> gd = new GenericDemo<Integer>(); gd.classPrint(1); gd.setX(2); //---------------------- //泛型方法,與調用的類型保持一致,參數類型得為String GenericDemo.<String>methodPrint("abc"); //--------------------- //泛型接口,與初始化時候傳入的類型保持一致,參數類型得是Double IFC<Double> ifc = new IFC<Double>() {}; ifc.interfacePrint(2.9); } private C c; public void setX(C c) { this.c = c; } public void classPrint(C c) { System.out.println(c.getClass()); } //泛型方法,前面的<T>是為了標識這是一個泛型方法 public static <T> void methodPrint(T t) { System.out.println(t.getClass()); } interface IFC<I> { //泛型接口 default void interfacePrint(I i) { System.out.println(i.getClass());} } }
3,通配符
3.1, T和?的區別
基本泛型T是用於定義,將數據類型參數化,不能用於實例化。而 ? 則是在實例化對象時不確定泛型具體參數類型的時候泛指Object的所有子類型。
類型 | 特點 |
---|---|
T | < T extends Object>,用於定義 |
? | < ? extends Object>,用於實例化 |
?不能和Object等效,?是類型實參而不是類型形參,它用於泛指各種類型實參,當具體類型不確定的時候就可以使用?,示例如下:
public class test6 { public static void main(String[] args) { List<String> list1 = new ArrayList<>(); List<Integer> list2 = new ArrayList<>(); test(list1); test(list2); } public static void test(List<?> list) { System.out.println(list); } }
3.2,上下界通配符
上下界通配符其實涉及到java 的多態屬性,上下轉型的可行性,子類實例可以轉換成父類實例,但是父類實例卻不一定能轉換成子類實例,只有本身就是該子類實例向上轉型的父類實例才可以向下轉型為子類實例。
<? extends T> 表示類型範圍為T以及其子類,<? super T>表示類型範圍為T及其父類。
界限通配符在應用於集合的時候會影響集合的讀寫行為:
上界<? extends T> 限制了類型上限,只能向上轉型,可以讀,但是沒法寫,因為子類型不確定,沒法向下轉型;
下界<? super T>限制類型的下限,只能向下轉型,可以寫,但是沒法讀,因為父類型不確定,沒法向上轉型。
示例:
public class test { public static void main(String[] args) { //<? extends B> 範圍: A類或者A的子類 //由於下限不確定,所以無法向下轉型至具體類型 List<? extends B> list1 = new ArrayList<B>(){{add(new B());}}; // list1.add(new B()); //無法添加該類型, 向下轉型無法確定目標類型 // list1.add(new C()); A a = list1.get(0); //正常向上轉型 //<? super B> 範圍: B類或者B的父類 //由於上限不確定,所以B類和B類的子類均可以加入,但是B類的父類不行 List<? super B> list2 = new ArrayList<>(); // list2.add(new A()); //無法向下轉型 list2.add(new B()); //正常向上轉型 list2.add(new C()); // C c = list2.get(0);//無法向下轉型,不加強制轉換會報錯 C c = (C)list2.get(0); } // A -> B -> C static class A {}; static class B extends A {}; static class C extends B {}; }
4, 附加約束(&)
AdditionalBound 的語法如下:
TypeBound: extends ClassOrInterfaceType {AdditionalBound} AdditionalBound: & InterfaceType
也就是說extends後面可以在加個額外約束,具體為接口類型,可以I1 & I2 & I3這樣連排,注意必須是接口類型,不能是class或者類型變量,這裡額外約束的作用是限制類型必須實現相關的接口,示例如下:
public class test { public static void main(String[] args) { test1(1); test1("1"); test2(2); test2("2"); // test3(3); //test3方法String類型才滿足額外約束 test3("3"); } public static <T extends Object> void test1(T t) { System.out.println(t.getClass()); } //得同時實現Serializable和Comparable接口 public static <T extends Object & Serializable & Comparable> void test2(T t) { System.out.println(t.getClass()); } //得同時實現Serializable,CharSequence和Comparable接口 public static <T extends Object & Serializable & CharSequence & Comparable> void test3(T t) { System.out.println(t.getClass()); } }
此外,附加約束還可用於類型強制轉換:
public class test12 { public static void main(String[] args) { System.out.println(test()); } public static Object test() { // return (Object & Number)"abced"; //編譯不通過 // return (Object)"abced"; //編譯通過 return (Object & CharSequence & Comparable)"abcde"; //編譯通過 } }
在一些類型轉換的場景可以通過附加約束控制類型轉換的範圍。
參考鏈接:
https://codeday.me/bug/20190313/767875.html
https://juejin.im/post/5b614848e51d45355d51f792
https://www.cnblogs.com/whatlonelytear/p/11055126.html
https://blog.csdn.net/s10461/article/details/53941091
https://blog.csdn.net/claram/article/details/51943742