泛型 :参数化类型。(将类型确认推迟到创建对象或者调用方法的时候来确认)
为啥要使用泛型?
在没有泛型的时候一般使用Object类来指定不确定参数的类型。但是这样一来会导致我们需要做额外的类型转化操作。
用法:可以用在类和方法上;用在类上紧跟类名之后,用在方法上需要先声明 声明在方法返回值类型之
前一般用<T>,<E>来指定;
public class Apple<T>{
//此种方法不是泛型方法
public void eat(T t){
System.out.println(t.getClass().getName());
}
//泛型方法
public <E> E palywith(E e){
return e;
}
}
通配符 ?(pecs原则 即producer extends consumer super )
对于<T>此种类型的泛型我们一般当做某种待确定的类,注意是一种。然而有些时候我们需要处理一个范围内的类型所以这个时候就需要用到通配符<?>
// <?>被叫做无限定通配符
List<Number> numberList;
List<Double> doubleList = new ArrayList<>();
//这种会编译出错,java不支持list型变,List<Double>不是List<Number>的子类
numberList = doubleList;
List<?> numberList;
List<Double> doubleList = new ArrayList<>();
numberList = doubleList;
//编译不通过
numberList.add(0.01)
//可以使用
doubleList.add(0.01);
System.out.println(numberList.get(0));
<? extends T> 被称作有上限的通配符也叫泛型协变。
List<? extends Number> numberList = new ArrayList<Double>();
//编译不通过
numberList.add(0.01);
//可以使用
numberList.get(0);
<? super T> 被称作有下限的通配符也叫泛型逆变。
List<? super Son> sonList;
List<Father> fatherList = new ArrayList<>();
sonList = fatherList;
fatherList.add(new Father());
sonList.add(new Son());
System.out.println(sonList.get(0).getClass());
System.out.println(sonList.get(1).getClass());
<?>一般配合集合使用,但是在使用中涉及到具体元素操作时是不被允许的。因为其本身就表示的是一个不确定
范围内的类型,我们不能调用集合的add()等方法,可以使用其get()等操作。有人说,<?>提供了只读的功
能,也就是它删减了增加具体类型元素的能力,只保留与具体类型无关的功能。它不管装载在这个容器内的元
素是什么类型,它只关心元素的数量、容器是否为空。
泛型擦除
泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除
型变:数组和泛型有所不同,假设Foo是Bar的一个子类型(子类或者子接口),那么Foo[]依然是Bar[]的子类
型,但G<Foo> 不是 G<Bar> 的子类型。Foo[]自动向上转型为Bar[]的方式被称为型变,也就是说,Java的数
组支持型变,但Java集合并不支持型变。