Java的泛型(generics)
前面写了一篇文章专门用来讲HashMap的,确实HashMap在平时是非常常用的,但是其中所涉及的内容还远远不止这些,举个例子:
HashMap<String,String> hashMap = new HashMap<>();
hashMap.put(1,"");//Error
首先我们知道,定义了一个键值对类型都是String类型的HashMap,如果你往里面丢入一个类型为非String类型的键值对,那么肯定是会报错的。但是如果把尖括号及里面的内容去掉的话,程序是可以通过编译的。
HashMap hashMap = new HashMap<>();
hashMap.put(1,"");//Pass
那么你会问,何苦要去掉括号呢?我明确的知道键值对的类型不是很方便嘛。确实,明确类型之后,编译器可以帮我们检查出很多插入数据类型不正确的问题,但是要知道泛型是在JDK1.5才出现的,那么在那个没有泛型的年代,是怎么确保类型安全的呢?
1.在没有泛型的年代,如何确保类型安全?
要知道,大多数的语言在最开始的时候都是没有泛型的例如C#,Go(现在都不支持),Java。就拿前面例子中的hashMap来说,在JDK1.5之前,可以在其中放入任何类型的数值。但是问题来了,如果我们想构造一个只能传入<String,String>类型的HashMap要怎么做呢?可以用类似装饰器模式来实现,见代码:
public class MyStringHashMapDecorator {
HashMap hashMap = new HashMap();
public void put(String key , String value){
hashMap.put(key,value);
}
public HashMap get(String key){
return (HashMap) hashMap.get(key);
}
}
但是问题来了,对于茫茫可能发生的情况,按照这样的话,我岂不是需要每一种都列出对应的状态。所以在JDK1.5中推出了泛型,简化了这些麻烦的操作,但是Java为了向后兼容性,所以这种泛型只是在编译期间存在的,运行期间还是会通过类型擦除(Type Erasure),去掉泛型的。这一点可以通过看字节码可以看出:
问题的答案是:是也不是,为什么这么说呢?前面提到,在运行期间经过类型擦除之后,他们都回归为ArrayList。说不是的原因在于他们之间不能相互赋值:
public static void main(String[] args) {
foo(new ArrayList<String>());//Error
}
public static void foo(ArrayList<Object> objects){
//do something
}
2.泛型的绑定
下面通过几个例子来引出泛型的使用:
- 1.extends -限定泛型必须是本身或者是其子类(上边界)
比如现在要比较两个参数的大小,但是参数的类型不确定,那么就可以用到泛型的绑定了。
举例说明:
public static void main(String[] args) {
System.out.println(max(1,2));//int
System.out.println(max(1.0,2.0));//double
System.out.println(max(1.0f,2.0f));//float
System.out.println(max(1L,2L));//long
System.out.println(max("abc","abd"));//long
}
private static <T extends Comparable<T>> T max(T a, T b) {
return a.compareTo(b) > 0 ? a : b;
}
结果如下:
2
2.0
2.0
2
abd
Process finished with exit code 0
但是在这就有疑问了:
1.为什么Comparable明明是个接口,却要用extends关键字?
2.如果对两种都实现了Comparable接口,但是对二者类型不同的参数进行比较,会有什么结果呢?
回答:
1.在泛型中,关键字extends后面既可以跟类,也可以跟接口。但是在实际意义上更接近于是绑定类型的自身或者子类型,所以用extends关键字更加接近。
2.见代码:
- 2.super-要求泛型必须是本身或者其父类型(下边界)
举例说明:(Cat继承了Animal类)
public static void main(String[] args) {
//在ArrayList中指定了T的类型,第二个参数传入的是T类型的父类型
sort(new ArrayList<Cat>(),new AnimalComparator());
}
private static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
}
class AnimalComparator implements Comparator<Animal> {
@Override
public int compare(Animal o1, Animal o2) {
return 0;
}
}