java学习笔记:泛型
Posted 滴滴哒滴哒
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java学习笔记:泛型相关的知识,希望对你有一定的参考价值。
泛型是一个很重要的概念。其实个人感觉它就像孙悟空保护唐僧画的一个圈,没画圈之前,什么妖魔鬼怪都可以在里面作祟,画圈之后,限定了只有唐僧才能待在里面。
在java使用泛型之前呢,以ArrayList为例,里面的元素就是Object,这样我们在使用add()方法的时候,无论是什么都会加进去,但是当我们get(),就必须进行强制类型转换。我们想做一个只维护String类型的List,但是java内部维护的却是一个Object的类型List,我们一不小心加入了Integer,在编译的时候不会有任何错误,但是在运行的时候会出现问题(ClassCastException异常)。
java5以后就引入了“参数化类型”的概念。这样我们在编译的时候就会发现错误,而不是在运行的时候,再去发现错误 。
一、伪泛型
java的泛型只是“伪泛型”,因为历史原因(兼容性:低版本的程序能在高版本上跑得动)。 看一个例子
public class MyGenericity
public static void main(String[] args)
ArrayList<String> arrayString=new ArrayList<String>();
ArrayList<Integer> arrayInteger=new ArrayList<Integer>();
System.out.println(arrayString.getClass()==arrayInteger.getClass());
结果输出是true。明明是两个不同的List,一个是String的List,一个是Integer的List,为什么结果反而是true呢。这是因为java编译器使用了类型擦除来实现,它把泛型版本转换为非泛型版本,所有尖括号之间的信息都被扔掉了。
再通过一个例子,来证明java进行了类型擦除,通过反射的方式来添加元素。
public class MyGenericity
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException
ArrayList<Integer> arrayList3=new ArrayList<Integer>();
//这样调用add方法只能存储整形,因为泛型类型的实例为Integer
arrayList3.add(1);
arrayList3.getClass().getMethod("add", Object.class).invoke(arrayList3, "one");
for (int i=0;i<arrayList3.size();i++)
System.out.println(arrayList3.get(i));
输出的结果
可以看到我们设置的存储Integer类型的List,却存储了String。
二、类型擦除
当定义一个泛型类型,都自动提供了一个原始的类型。编译的时候,擦除类型变量,并替换为原始类型(无限定类型则替换为Object(限定一般用extends))。假如有多个限定<T extends Comparable&Serializable>,那么原始类型就是第一个Comparable(这里的extends并不是狭义上的继承,所以可以去连接一个或者多个接口)。
在虚拟机中没有泛型,所有的类型参数都用它们的原始类型替换。可以看到java的泛型只到编译阶段。所以也会在使用过程中造成一些问题:
1、不能用基本类型实例化类型参数
2、运行时类型查询只适用于原始类型
3、不能实例化类型变量
4、不能构造泛型数组
5、泛型类的静态上下文中类型变量无效
6、不能抛出或捕获泛型类的实例
7、注意要消除类型擦除引起的一些问题,特别是泛型类中的应用,例如
public class Pair<T>
public boolean equals(T value)
//TODO
我们在使用的时候以为用的是我们自身定义的方法,其实Object的方法equals()。补救的方式就是重命名方法名。
还有就是当我们使用<? extends A&B> AB两个接口不能是同一接口的不同参数化,这样重名的方法,可能会造成影响。
可以见得使用泛型的时候,虚拟机内部有类型转换的代码。虽然使用泛型可能使得效率降低,但是为了保障系统的稳定,还是推荐使用泛型。
三、泛型类
泛型的使用某种意义上来讲,可以增加我们代码的重用性,有时候感觉跟工厂模式有点异曲同工之妙。
public class Apple<T>
private T info;
public Apple()
public Apple(T info)
this.info = info;
public void setInfo(T info)
this.info = info;
public T getInfo()
return this.info;
使用的时候需要我们将T替换成具体的类型。
public static void main(String[] args)
Apple<String> apple1 = new Apple<>("APPLE");
System.out.println(apple1.getInfo());
Apple<Integer> apple2 = new Apple<>(33);
System.out.println(apple2.getInfo());
如果要别的类去继承,需要指明T的类型是什么。
public class GreenApple extends Apple<String>
或者不加菱形符号(调用的时候可能就要用Object去定义取出了)
public class RedApple extends Apple
四、泛型方法
还可以在普通类中定义泛型方法
public class GenericMethod
public static <T> T getMiddle(T t)
return t;
使用
public class GenericDriver
public static void main(String[] args)
String a = GenericMethod.<String>getResult("aaa");
五、泛型通配符
以字母代替的时候,当我们使用的时候,还是要去确定是什么样的具体类型,如何应对不确定的类型,这时就可以用到我们的通配符?了。
public class GenerictyTest
public List<?> getUnKonwn(String s)
if (s == "Stirng")
List<String> stringList = new ArrayList<>();
stringList.add("one");
return stringList;
else if (s == "Number")
List<Integer> intList = new ArrayList<>();
intList.add(111);
return intList;
else
List<Long> longList = new ArrayList<>();
return longList;
但是通配符的缺点就是你无法在不知道“?”是什么的时候,使用任何与泛型相关的方法,也就是说你无法在这个方法体里面使用add()这种方法,但是可以使用get()方法:Object o = unKnownList.get(0);。
六、带边界的泛型通配符
<? extends ClassName>类型是ClassName本身或者它的子类,上界通配符
<? super ClassName>类型是ClassName本身或者它的父类,下界通配符
用法上extends会使得set()方法失效,但get()方法仍然有用。get()取出来的也只能放在ClassName本身或者它的基类下。
super怎会使得get()方法部分失效,只能放到Object中,set()方法正常。
以上是关于java学习笔记:泛型的主要内容,如果未能解决你的问题,请参考以下文章