java 泛型 详解_Java泛型详解(透彻)

定义

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值