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

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