为什么会有泛型
我们先从一个老生常谈的问题说起:
List list = new ArrayList();
list.add(1);
list.add("2");
for(int i = 0; i < list.size(); i ++){
System.out.println((Integer)(list.get(i)) + 1);
}
上面的代码中,当执行到第二个循环便报错了.这是因为我们传进去的是字符串的"2",但是字符串"2"不能强制转成Integer 2, 所以报错.
之所以会出现这种情况,是因为我们的加法运算只能是整型,但是我们的list鱼龙混杂,什么都可以加进去,那么有什么方法指定list只能添加某种类型,那肯定是有的.那就是使用泛型.我们再来看下面的代码
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add("1");
for(int i = 0; i < list.size(); i ++){
System.out.println((Integer)(list.get(i)) + 1);
}
上面的代码别说运行,别说编译,就是放在idea,它也给list.add("1");
报个红线处理.
所以泛型有什么用呢?泛型能像参数一样传递类型,它能使编译器可以在编译期间对类型进行检查以提高类型安全,减少运行时由于对象类型不匹配引发的异常.
我们再来看一个例子
假如没有泛型,我们想抽象可以放东西的一个盒子怎么抽象呢?我们大概会抽象盒子里面有一个object对象,用来存放东西,代码大概如下:
public class Box{
private int weight;
private Object object;
...get/set...
}
这时我们有一只猫
public class Cat{
private String name;
private int age;
...get/set...
}
我们想把猫放进盒子坐火车回家,等下了火车就取出来:
public static void main(String[] args) {
//把猫藏到盒子里面
Box box = new Box();
box.setObject(new Cat());
//坐火车
train();
//把猫取出来
Cat cat = (Cat)box.getObject();
}
有没有发现,每次我们把猫拿出去来的时候都需要先把猫强转一下,都会让我们的代码看起来很不优雅..
怎么制作一个泛型
普通泛型
如果有了泛型,上面箱子会变成什么样子呢
用了泛型的箱子
public class Box<T>{
private int weight;
private T object;
public Box(T object){
this.object = object;
}
...get/set...
}
在类型后面加上<T>,即完成了一个简单的泛型,这里的T表示一个Java类型,我们把一个Java类型传入T,然后Box里面所有用T修饰的类型都会变成我们传入的Java类型了.也就是说我们可以理解成传入Cat的Box就变成这样子了
public class Box<Cat>{
private int weight;
private Cat object;
...get/set...
}
除了可以用T,我们还可以用其它字母,之所以用T更多的是因为前辈们的约定,类似的约定还有
- ? 表示不确定的java类型
- T (type) 表示具体的一个java类型
- K V (key value) 分别代表java键值中的Key Value
- E (element) 代表Element
现在我们要把猫拿到箱子里过火车怎么做呢?
//把猫藏到盒子里面
Box<Cat> box = new Box();
box.setObject(new Cat());
//把猫取出来
Cat cat = box.getObject();
现在不用强制转换了,代码看起来也顺眼了许多
多个泛型
static class Hmap<K,V>{
private K key;
private V value;
...get/set...
}
这里没什么好讲的,跟一个的差不多;三个参数,四个参数也一样,无非是多几个逗号
泛型边界
Class BlueCat extends Cat{}
Class RedCat extends Cat{}
Class Cat extends Animal{}
-
<? extends T> 表示类型的上界,表示参数化类型的可能是T 或是 T的子类
Box<? extends Cat> box = new Box<BlueCat>(new BlueCat());
Box<? extends Cat>
表示一个放了可能是蓝猫或者虹猫或者猫的箱子
Cat cat = box.getObject()
因为箱子里面放的可能蓝猫,虹猫或者猫,这些都是猫,所以拿出来的肯定可以用Cat来接收
但是box.setObject(new BlueCat())
就报错了,因为这是个可能房蓝猫,虹猫或者猫的箱子,你放了个蓝猫进去,万一这是个放红猫的箱子怎么办? -
<? super T> 表示类型下界(Java Core中叫超类型限定),表示参数化类型是此类型的超类型(父类型),直至Object
Box<? super Cat> box = new Box<Cat>(new BlueCat());
Box<? super Cat> box
表示这是一个放了猫的基类的箱子,猫的基类有什么呢?有动物Animal,有Object等.也就是说这是一个可能是放了Animal,Object,Cat的箱子
Cat cat = box.getObject()
这个会报错,因为这个这是一个放了可能是Cat,Animal,Object的箱子,你拿一个Cat来接收,万一它是Animal怎么办,就接收不了了啊.
box.setObject(new BlueCat())
这个就可以执行,因为你BlueCat它是Cat的派生类啊..他也是Animal的孙子类啊.更不用说是Object的子类了
box.setObject(new Dog())
假如我们这个dog继承了Animal,这里还是报错,这里只是说可能是放了Cat,Animal,Object的箱子,万一放的是Cat那和Dog就没有什么关系了.
泛型方法
我们在方法的返回值类型前面加一个<T>,表示这是一个支持泛型的方法,然后我们就可以把返回值类型,参数类型等设为泛型了.其他跟泛型类差不多
//这是我写的一个用于取数组前length个元素的方法,当list的长度小于length时,返回这个list,
//因为假如直接list.subList(0,length),当length大于list的实际长度会报错
public static <T> List<T> subList(List<T> list, Integer length) {
if (list.size() <= length) {
return list;
}
return list.subList(0, length);
}
网友评论