java-day33
文章目錄
泛型的邊界(上限和下限)
public void test1(Collection<?> c){
for(Object obj:c){
System.out.println(obj);
}
}
注意,這裏直接使用通配符?,表示將來可以接收任意類型的泛型集合對象
使用extends關鍵字可以設置泛型的上限。
這個表示的範圍是比較廣泛的,很多時候,我們希望把這個範圍再限定一下,縮小點範圍。
public void test1(Collection<? extends Number> c){
for(Object obj:c){
System.out.println(obj);
}
}
這個時候方法的參數就表示,只能接收泛型類型是Number或者Number【子類型】的集合對象。
使用super關鍵字可以設置泛型的下限。
public void test1(Collection<? super Number> c){
for(Object obj:c){
System.out.println(obj);
}
}
這個時候方法的參數就表示,只能接收泛型類型是Number或者Number【父類型】的集合對象。
例如:
List<Number> list3 = new ArrayList<Number>();
List<Object> list4 = new ArrayList<Object>();
List<java.io.Serializable> list5 = new ArrayList<>();
//Number繼承了Object並實現了Serializable接口
//Object和Serializable都屬於Number的父類型
//這三個代碼編譯都是通過的
t.test(list3);
t.test(list4);
t.test(list5);
總結:
使用extends可以定義泛型的【上限】,這個就表示將來泛型所接收的類型【最大】是什麼類型。可以是這個最大類型或者它的【子類型】。
使用super可以定義泛型的【下限】,這個就表示將來泛型所接收的類型【最小】是什麼類型。可以是這個【最小類型】或者它的【父類型】。
泛型中extends和supper的使用場景
extends 限定泛型的上限
1.【可以】在聲明泛型類或者泛型接口的時候使用
注意,這裏extends後也可以跟接口
public class Point<T extends Number>{
private T x;
private T y;
}
public interface Action<T extends Person>{
public void test(T t);
}
2.可以在聲明泛型方法的時候使用
public <T extends Action> void test(T t);
注意,在泛型中使用extends的時候,後面可以跟【接口】類型。當前這個例子就表示,將來這個泛型T所接收的類型只能是Action接口類型或者Action子接口類型或者這些接口的實現類類型。
3.可以在聲明變量的時候使用
注意,集合泛型中使用了?通配後,就不能再進行add添加數據了。
List<? extends Number> list = new ArrayList<Integer>;
list = new ArrayList<Double>;
list = new ArrayList<Long>;
//不能添加數據
//?通配後,指向對象的具體泛型不能確定,所以編譯不允許添加數據
list.add(1);
在方法的參數中也可以使用extends
public void test(List<? extends Number> list){
}
super 限定泛型的下限
1.在聲明泛型類和泛型接口中【不能】使用super
//編譯報錯
public class Point<T super Number>{
private T x;
private T y;
}
//編譯報錯
public interface Action<T super Person>{
public void test(T t);
}
2.在聲明泛型方法的時候【不能】使用super
//編譯報錯
public <T super Action> void test(T t);
3.在聲明泛型類型的變量時候【可以】使用super
List<? super Number> list;
list = new ArrayList<Number>();
list = new ArrayList<Object>();
假設Student 繼承了 Person
List<? super Student> list;
list = new ArrayList<Student>();
list = new ArrayList<Pesson>();
list = new ArrayList<Object>();
注意,在定義方法參數列表的時候,也是在聲明變量,這個時候也可以使用super
public void test(List<? super Number> list){
}
總結,extends和super其實都是給泛型的類型指定一個限定的範圍,因爲如果不指定的話,一個泛型的類型將來就可以使用java中任意一個類型,這樣的話範圍就太廣泛了,不好控制。
例如:
public class Point<T>{
private T x;
private T y;
public void test(List<? extends T> list){
if(list.size()==2){
this.x = list.get(0);
this.y = list.get(1);
}
}
public String toString(){
return "point[x="+x+",y="+y+"]";
}
}
main:
//當使用Point類並指定泛型的類型爲Number的時候
//Point類中的所有T就變成了Number
Point<Number> p = new Point<Number>();
//test方法變爲 test(List<? extends Number> list)
List<Long> list = new ArrayList<Long>();
list.add(1L);
list.add(3L);
//所以下面代碼編譯運行都可以
//List<? extends Number> 和 List<Long> 是兼容的
p.test(list);
System.out.println(p);
----------------------------
//當使用Point類並指定泛型的類型爲Integer的時候
//Point類中的所有T就變成了Integer
Point<Integer> p = new Point<Integer>();
//test方法變爲 test(List<? extends Integer> list)
List<Long> list = new ArrayList<Long>();
list.add(1L);
list.add(3L);
//所以下面代碼編譯報錯
//List<? extends Integer> 和 List<Long> 不兼容
p.test(list);
System.out.println(p);
原始類型(raw-type)
//使用泛型接口和泛型類,同時指定泛型的類型
List<String> list = new ArrayList<String>();
//使用泛型接口和泛型類,但是[沒有]指定泛型的類型
//使用泛型的原始類型raw-type(默認Object)
List list = new ArrayList();
注意,泛型的原始類型,其實就是Object,
所以在指定泛型的時候,類中的泛型就會默認使用Object
泛型類型信息擦除(Type Erasure )
類型擦除只存在於編譯期間的源碼中,編譯成class文件後,class文件中將會擦除所有泛型類型相關的信息。
所以ArrayList 和ArrayList在編譯期間是不同類型(不兼容的),因爲編譯器會根據泛型的信息做類型的安全檢查編譯成class文件後,這個倆個和ArrayList表示的是同一種類型
//注意,這裏編譯會報錯
//List<String> List<Integer> 編譯的時候雖然不同
//但是編譯後泛型信息會被擦除
//List<String> List<Integer> 就變成相同的類型
//類中相當於有了倆個一模一樣的方法,編譯報錯
//例如:
public void run(List<String> list){
}
public void run(List<Integer> list){
}
//例如:
ArrayList<String> list1 = new ArrayList<String>();
ArrayList<Integer> list2 = new ArrayList<Integer>();
//判斷list1所指向對象的實際類型 和 list2指向對象的實際類型
//是否相等
//結果輸出爲:true
//原因:編譯成class文件後,泛型信息被擦除
System.out.println(list1.getClass() == list2.getClass());
總結:
1、聲明
//Point.java
public class Point<T>{
//...
}
2、編譯
Point.java ---編譯---> Point.class
3、使用
Test.java
main:
Point<Object> Point<String> Point<Integer> ....
//在編譯期間,以上這些類型,相互之間都是不兼容的
//思考:有沒有一種類型,可以和以上所有類型兼容? 有的,需要使用通配符:Point<?>
//編譯報錯,類型不兼容
Point<Object> p = new Point<String>();
//編譯完成後,以上這些類型都是同一種類型,都對應Point類
//因爲編譯成功後,Test.class文件中,會把main方法裏面的Point指定的泛型信息給擦除掉。
泛型例子
@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}