java 泛型怎么理解_Java中泛型的理解

Java中的泛型,本质上来说,就是是参数化类型,就是说所操作的数据类型被指定为一个参数,而不是确定的某种类型。这种数据类型可以用在类、接口和方法创建中。即泛型类、泛型接口、泛型方法。这样说可能不够生动,来举些例子说明一下。

例子一

我们通过 例子一来简单看看泛型的应用场景。下面的情况中,没有使用泛型:

public classFanXingTest {public static voidmain(String[] args) {

ArrayList arrayList= newArrayList();

Integer integer1= 1;

arrayList.add(integer1);//你记得这个元素的类型,那么没问题,通过index取出来

Integer integer2 =(Integer) arrayList.get(0);

System.out.println(integer2);//假如你记错了arraylist里面存的类型了,这时编译器也不会报错,但是运行时会报错

String string =(String) arrayList.get(0);

System.out.println(string);

}

}

ArrayList里存放的是Object类。这样的好处是:通过对类型Object的引用来实现可以存放任意类(Java的多态机制),但也带来了缺点:需要进行强制类型转换。

可以看到,在没有使用泛型的情况下,你若记得 arrayList 里面存了什么类型的东西,那么最好不过了,可以直接通过index来取出来,并进行类型转换即可。

而假如你在不知道的情况下强制转换写错,编译器也不会提示错误,在运行时才会发现,会报一个错: java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 。实际情况中程序员不可能对每个元素都记得其类型,所以这样有明显的不合理性。

泛型的引入可以解决上述问题,先来看看针对上述情景使用泛型的情况:

public classFanXingTest {public static voidmain(String[] args) {

ArrayList arrayList = new ArrayList();

Integer integer1= 1;

arrayList.add(integer1);//直接取出来,无需强制类型转换

Integer integer2 = arrayList.get(0);

System.out.println(integer2);//编译器会检查出错误,根本不允许你把类型搞混了,下面两种情况编译器都直接提示不能类型转换

String string1 =(String) arrayList.get(0);

String string2= arrayList.get(0);

}

}

上面的代码中我们使用了泛型,就是在创建 arrayList 的时候,就限定了里面存放的只能是 Integer 类型的数据,那么编译时就可以检查类型安全,所有的强制类型转换是自动和隐式的,提高代码重用率。我们在取出数据的时候,就无需进行强制类型转换,而且你把类型搞错,编译器会提示错误。

例子二

那么泛型是如何实现的呢?自己如何创建泛型类?我们通过 例子二 来看一看。先自己来创建一个泛型类,如下所示:

//自己新建一个泛型类:MyFan

class MyFan{privateT t1;//构造函数

publicMyFan(T t1){this.t1=t1;

}//测试泛型的操作数据类型

public voidshowInfo(){

System.out.println("所操作数据类型是:"+t1.getClass().getName());//通过反射机制,可以知道T的各种成员变量和方法的信息

Method m[]=t1.getClass().getDeclaredMethods();for (int i = 0; i < m.length; i++) {

System.out.println(m[i].getName());

}

}

}

自己创建泛型类的时候,先假设这样一个处境:我希望我的这个类可以处理多种类型的数据。这就需要把类型"参数化",也就是当成一个"参数"传进来。所以定义类的时候,在类名的后面跟了一个: class MyFan ,这个T只是一个象征意义的符号,可以是任意符号。T 就是个类型"参数",我们之后可以传任意类型的类进来。上面的 showInfo() 方法中,我们可以利用反射机制,来得到传进来的这个数据类型是什么,也可以得到这个类里面有些个什么方法。接下来,我们利用我们自己创建的泛型类 MyFan 来做个试验:

public classFanXingTest {public static voidmain(String[] args) {//TODO Auto-generated method stub

MyFan ft1=new MyFan("黄黄");

ft1.showInfo();

MyFan ft2=new MyFan(1);

ft2.showInfo();//输出两个实例对象的类型,结果是一样的

System.out.println(ft1.getClass());

System.out.println(ft2.getClass());

}

}

通过创建我们自己的泛型类的实例, ft1 里面的 t1 变量指向一个String类实例, ft2 里面的 t1 变量指向一个Integer类实例,那么上述程序的输出如下:

所操作数据类型是:java.lang.String...String的各个成员方法略去

所操作数据类型是:java.lang.Integer...Integer的各个成员方法略去

class MyFan

class MyFan

最后两行的输出,我们是想来通过 ft1/ft2.getClass() 来看看创建的这两个实例的类型会不会不一样,结果可以看到,两个都是属于 MyFan类的实例变量,并没有因为传入类型的不同而引起不同。

例子三

现在应该知道了泛型是如何运作的,我们在自己定义泛型类或者泛型接口的时候,都是跟在类名的后面: class MyFan{...} ,然后实例化的时候也跟在类名的后边即可: MyFan fan1 = new MyFan()。

有些时候我们还会遇到更为复杂的情况,这几天我在工具类Collections里面,看到了sort方法,如下:

public static > void sort(Listlist) {

list.sort(null);

}

我们需要知道extends后面跟的类型表示泛型的上限(含自己),super表示的是泛型的下限(含自己)。如,表示T必须是该指定类/接口的子类(实现了某接口或继承自某类,都算"子类")。

我们来看 > ,首先规定了T必须是 Comparable super T> 的一个子类,也就是说,T 必须是实现了 Comparable super T> 这个接口的。然后 super T>表示Comparable<>中的类型下限为T(就是至少得是T的父类,包括T)!我们可以通过 GregorianCalendar 类和 Calendar 类来加深理解。先看下两个类的定义:

//Calendar类实现了Comparable接口

public abstract class Calendar implements Serializable, Cloneable, Comparable{

...

@Overridepublic intcompareTo(Calendar anotherCalendar) {returncompareTo(getMillisOf(anotherCalendar));

}

}//GregorianCalendar类继承自Calendar类

public class GregorianCalendar extends Calendar{...}

那我们现在假设有个ArrayList,调用上面的sort方法:

ArrayList arrayList = new ArrayList();

Collections.sort(arrayList);

此时不会报错,因为相当于 > 是成立的,我们结合两个类的定义可以知道:Calendar为GregorianCalendar 的父类,并且Calendar实现了Comparable,于是GregorianCalendar 也实现了Comparable接口。所以泛型的限制范围 > 是满足的。接下来我们自己定义一个sort函数:

public static > void sort(ArrayListlist) {

list.sort(null);

}

此时调用我们的sort函数 sort(new ArrayList());,编译器就会报错,因为GregorianCalendar类只是实现了Comparable接口,并没有实现Comparable接口。于是传入的参数不满足泛型的限制范围,所以会报错

自己的一些思考

先看看下面的Collections类里面的两个sort方法

public static > void sort(Listlist) {

list.sort(null);

}public static void sort(List list, Comparator super T>c) {

list.sort(c);

}

注意到上面除了方法参数(比如List list)之外,都是在返回类型(void)之前对方法参数类型进行了限定(比如static > void)。

其实泛型就是”可有可无”,它的存在就是限制了你将要使用的参数类是什么范围内,extends/super分别是泛型的上下界(含)。就比如上面第一个sort,限定了>,第二个就是没限定,任意的;但是还有参数这块,第一个的参数就是之前限定好的参数T即可;第二个的参数呢,在Comparator super T>这里限定了一个下届,得是T的父类。

再往下说,为什么是>而不是>呢。这其实考虑到了子类若是继承自父类,而父类已经实现了Comparable接口,子类可以拿来用,所以就有>的效果,举个例子就是上面的GregorianCalendar类和Calendar类,等效于子类GregorianCalendar也实现了Comparable接口。这时候若是>就不合适了。

其实一般自己实现的Comparable接口的话,都是 class MyTest implements Comparable这种,估计也不会class MyTest implements Comparable,上面的>只是考虑到了比如ManmanTest继承自MyTest,那么ManmanTest也就实现了Comparable接口的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值