用泛型控制集合元素
泛型即泛指任意类型,又叫参数化类型(ParameterizedType),对具体类型的使用起到辅助作用,常用在集合中,用于指定存放元素的类型;例如:
//等号前半部分指定泛型,等号后半部分在JDK7开始可以省略泛型指定
List<Integer> list=new ArrayList<>();
list.add(1);
list.add(2);
list.add("3");//添加非指定类型的元素就会编译报错
使用泛型的好处:类型安全,避免了取出元素后的类型转换
泛型类型只能是引用数据类型,若要指定基本数据类型,需要写基本数据类型的包装类名
定义泛型类
//在类名后面加泛型尖括号,里面注明泛型种类名称,举例:
public class 类名<T>{}
泛型种类名称并没有规定,无论使用哪个字母都能达到同样的效果,但是以下几个名称是常用到的:
E-Element(在集合中使用,因为集合中存放的是元素)
T -Type (java类)
K-Key (键)
V-Value(值)
N-Number(数值类型)
?-表示不确定的java类型
定义带有泛型的接口与带泛型的类类似
//在类名后面加泛型尖括号,里面注明泛型种类名称,举例:
public interface 接口名<T>{}
接口中的抽象方法可以使用泛型作为方法的参数或者返回值
//后面的这个泛型也可以省略掉(省略不会在实现类报错,但是后面不写,同样的方法就不能算作重写)
修饰符 class 类名<泛型> implements 接口名<泛型>
举个例子:
public interface TestInter<T> {//先定义一个接口,引入泛型T
public abstract int getNum(T t);//将T作为方法参数
public abstract T getT(T t);//同时作为返回值类型和方法参数
}
//这里TestInterImpl后面的T不写,就会报错,因为下面的实现方法用到了T,
//但如果上面全部泛型不写,下面的T类型改为Object类型的话也不会报错,且同样算作实现方法
//这里TestInterImplT写了,后面TestInter位置的T不写,在类定义这一行不会报错,
//但是下面方法会报错,因为这里的T没有限定和接口T一同做限定,就会被判断为不同的方法签名,不能算作实现方法
public class TestInterImpl<T> implements TestInter<T> {
@Override
public int getNum(T t) {
return 0;
}
@Override
public T getT(T t) {
return null;
}
}
定义带泛型的方法
修饰符 <泛型> 返回值类型 方法名(参数列表(此处可用泛型)){方法体}
普通方法和静态方法都可以使用泛型,传递什么类型的参数,泛型就是什么类型
普通方法和静态方法都可以使用泛型
泛型通配符
<?>代表未知的数据类型,即通配符,可以通过在泛型中对?作范围限制来控制集合中存储的数据类型
泛型上限:类型名称<? extends 类> 对象名称
意义:只能接收指定类型及其子类
泛型下限:类型名称<? super 类> 对象名称
意义:只能接收该类型及其父类
PECS原则(Prodecer Extends Consumer Super):
频繁向外读取内容的,适合限制上界extends
频繁向内吸入内容的,适合限制下界super
extends可以作为返回类型限定,不能用于参数类型限定
super可以用于参数类型限定,不能用于返回类型限定
?既不能用于方法参数传入,也不能用作返回值类型
举例:
public class CardTest {
public static void main(String[] args) {
/*
这里泛型的限制是P1的子类,但是是相对于集合内的元素来说的,
在存入元素时,并不会判断该存入的元素是否是P1的子类
因此第二行会编译报错
*/
List<? extends P1> list1=new ArrayList<>();
list1.add(new P2());
/*
这里限定了泛型是P1的父类,第二行就可以顺利存入了,这里的判断逻辑是:
P1的父类型 集合内元素=P2的子类型 赋值来的数据
这里就是多态的写法,对应的取出时就变成了
? 要接受P1的变量=P1的子类型 集合内元素
这样?处就只能时P1的父类了,没有定义P1的父类,因此只能拿Objext类型接收
*/
List<? super P1> list2=new ArrayList<>();
list2.add(new P2());
Object o = list2.get(0);
/*
这里同样,虽然没有存入元素,但是可以演示下取出元素时的逻辑
? 要接受P3的变量=P3的子类型 集合内元素
?处的类型只要是P3的父类都能顺利接收到,P1P2或者Object都可以
*/
List<? extends P3> list3=new ArrayList<>();
P2 p2=list3.get(0);
}
}
//下面简单定义了三个层层继承的类
class P1{
}
class P2 extends P1{
}
class P3 extends P2{
}