2020/4/6學習筆記day33

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));
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章