泛型在java中是非常常用的。
如果不使用泛型,创建一个ArrayList时,里面的元素都是Object类型的。所有类型的对象都可以保存到ArrayList中,例如String,Integer,Double类型等等,这给开发人员带来了很多不必要的麻烦。
因此,如果在创建ArrayList后面加上一个泛型,如代码:
ArrayList<Integer> arr = new ArrayList<>();
这样这个容器就只允许Integer 类型的对象存放,String、Double等其他类型的对象就不能存放进来了。
泛型简化代码实例
泛型的好处不仅如此,很多情况下,泛型的使用可以大大降低开发成本。例如设计一个栈,如果不使用泛型,则需要对Integer,String,Double等所有类型都分别进行设计不同的栈,造成严重的代码冗余;在这里,泛型就大显神通了。看一下通过泛型创建栈的代码:
public static class My_Stack <T> {
LinkedList<T> list = new LinkedList<T>();
//压栈
public void push(T item)
{
list.addLast(item);
}
//出栈
public void pull()
{
list.removeLast();
}
//取栈顶元素
public T top()
{
return list.getLast();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
My_Stack<Integer> s = new My_Stack<>();
s.push(3);
s.push(2);
System.out.println(s.top());
s.push(4);
System.out.println(s.top());
s.pull();
s.pull();
System.out.println(s.top());
}
在上面的代码中,通过在类声明时的后面加一个泛型< T>,就可以在实例化对象的时候任意指定这个T的类型(如代码中的Integer),同时这个对象里面含有T的成员变量或成员方法,也属于创建对象时指定的T的类型(代码中的Integer)。通过使用泛型,实现了对代码的减肥,搭建了任意数据类型都可以使用的栈。
不仅是在栈里面的应用,泛型还可以应用到很多数据结构中,例如二叉树、队列等。
泛型通配符
通配符主要分三种:<? extends T> 、<? super T> 以及 <?>
1. <? extends T>
对于 <? extends T>,表示这是一个T泛型或者其子类泛型。例如现在有三个类,分别是Number,Integer和Double。Integer和Double都是Number的子类,如果定义:
ArrayList<? extends Number> arr = new ArrayList<>();
此时arr的泛型可能为Number,可能为Integer,可能为Double。
在arr中取出任何一个元素,都可以向上转型为Number;
但是,如果要插入元素,Integer就不满足< Double>,同样地,Double也不满足< Integer>。
因此,如果希望 只取出,不插入,可以使用<? extends T>
2. <? super T>
对于 <? extends T>,表示这是一个T泛型或者其子类泛型。例如定义
ArrayList<? super Number> arr = new ArrayList<>();
则arr的泛型可能是Number类型,可能是其父类Object类型。
可以向容器中插入Number类型或Number子类的对象。
但是,如果取出元素会有风险,因为不确定是Number类型还是Object类型。
因此,如果希望 只插入,不取出,可以使用<? super T>