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