定义
Java中的泛型在JavaSE5中引入。
所谓泛型,即参数化类型。就是说,类型是以参数的方式传入泛型类。
例如:
ArrayList aaryList = new ArrayList();
那么,类型参数就是Integer。
缘由
为什么要引入泛型呢,得看在没有泛型的情况下会存在什么样的问题。看下面这个非常常见的例子:
ArrayList a = new ArrayList();
a.add(1);
a.add("2");
for(int i=0; i
{
String strTmp = (String)a.get(i);
}
点击运行,啊哦,ClassCastException!
看出来了吧,问题就是类型不安全,给你一个ArrayList,很难直接获取内部元素的类型,容易引发错误。
泛型设计中的坑
JavaSE5中怎么引入泛型呢?
原先没有泛型的时候使用方式是这样:
ArrayList a = new ArrayList();
如果老的方式直接不支持了,那么基于Java做得那么多类库咋办,岂不是没法直接迁移至新版本的jdk?
No, No, No~这样会损失很多用户~
算了算了,非泛型和泛型两种方式并存吧。
那么泛型怎么在以前的基础上加入呢?
想来想去,还是在编译器保证吧!最简单!
ok,存在什么问题呢?看个demo:
class GenericsA
{
T t = new T(); // Error
}
天真的我以为可以这么搞,然而我却被告知: Type parameter ‘T‘ cannot be instantiated directly.
好吧,原来这个T只是个占位符而已,类型信息并不会在运行时保存!类型信息会被擦除!
类型擦除
泛型的类型信息会被擦除,因此不要奢想在泛型类内部利用类型信息做任何事情。
至于使用泛型类的地方,编译器来做类型检查,但是也不足够安全。
看个例子:
public static void main(String[] args) {
ArrayList arrayList = new ArrayList();
arrayList.add("A");
arrayList.add("C");
arrayList.add(3);
String tmp = (String)arrayList.get(2);
}
啊哦,又是ClassCastException。
别忘记,ArrayList内部没有类型信息,在把ArrayList转为ArrayList非泛型列表之后,连编译器也无法正常类型检查了!
类型边界
在编译器类型检查之前,泛型的类型信息会被擦除至某个边界。
普通泛型class ClassA{}中类型会被擦除至Object。
class A
{
T item;
public A(T item)
{
this.item = item;
}
void test()
{
//此处,item只能调用属于Obeject类的方法
}
}
通过泛型类型上设置边界,我们可以实现类型的限定:
class Animal{
void talk(){}
}
class B
{
T item;
public A(T item)
{
this.item = item;
}
void test()
{
//此处,item可以调用属于Animal类的方法
item.talk();
}
}
甚至,可以设置多个边界:
interface Eatable {void eat();}
interface Drinkable {void drink();}
class B
{
T item;
void test()
{
//此处,item可以调用属于Eatable和Drinkable的方法
//不过,前提是,item同时是Eatable和Drinkable
//(这里只能通过多个接口实现的方式,或者一个基类和多个接口的方式,因为你无法使一个类继承自多个父类)
}
}
通配符
所谓通配符,作用就是匹配多种类型。
1. 上界通配符: < ? extends B >
意思是,可以匹配类型A及其所有子类,即类型的上界是B,无下界。
举例子:
class A
{
public void print()
{
System.out.println("A");
}
}
class B extends A
{
@Override
public void print()
{
System.out.println("B");
}
}
class C extends B
{
@Override
public void print()
{
System.out.println("C");
}
}
class MyArrayList extends ArrayList extends B>
{
}
那么MyArrayList可以被赋值时:
MyArrayList myArrayList = new ArrayList(); //ERROR
MyArrayList myArrayList = new ArrayList(); //OK
MyArrayList myArrayList = new ArrayList(); //OK
再来看下元素读写情况。
编译器内心独白:
>>>> 既然设了上界通配符,那么我MyArrayList中的元素实际类型可能为B和B的任何子类,那么为了确保类型安全,只有同时继承自B和所有B的子类的类型才能add。
>>>> 嗯?好像没有任何一种类型可用满足以上条件。
>>>> 好吧,上界通配符泛型中,myArrayList不接受add方法。
>>>> 哦,对了,任何以泛型类型为参数的方法我都不接受!
好吧,经历了编译器的内心一番搏斗,所有以泛型类型为参数的方法都没法使用了:
myArrayList.add(new B()); //ERROR
myArrayList.add(new C()); //ERROR
myArrayList.set(0, new B()); //ERROR
但是,get()、remove()方法等方法还是可以用的。
2. 下界通配符 ? super B
意思是,可以匹配类型B及其所有基类,即类型的下界是B,上界是Object。
接着举例子:
class MyArrayList extends ArrayList super B>
{}
那么:
MyArrayList myArrayList = new ArrayList(); //OK
MyArrayList myArrayList = new ArrayList(); //OK
MyArrayList myArrayList = new ArrayList(); //ERROR
MyArrayList myArrayList = new ArrayList(); //OK
上至Object,下至B,所有的实例都可以赋值给泛型为的myArrayList。
再次看下元素读写情况。
编译器:
>>>> 既然设了下界通配符,那么我MyArrayList中的元素实际类型可能为B和B的任何父类,那么为了确保类型安全,只有同时是B以及B的所有父类的类型实例才能add。
>>>> 想一下,哪些类型符合条件呢?恩,好像所有B和B子类的实例都满足条件。例如。new B()既是B又是Object,new C()一样可以向上转型至B和Obejct....
>>>> 当然,所有B及其子类的实例都可以add进去。
>>>> 那么,get方法?
>>>> myArrayList内部可能的类型那么多(B及其所有父类),get的返回值类型咋办呢,算了,用通用的Object吧,真实类型让那些码农自己判断去吧!
原文:https://www.cnblogs.com/xinxinBlog/p/10264653.html