Java的泛型

Java的泛型(generics)

\quad 前面写了一篇文章专门用来说HashMap的,确实HashMap在平时是很是经常使用的,可是其中所涉及的内容还远远不止这些,举个例子:安全

HashMap<String,String> hashMap = new HashMap<>();
       hashMap.put(1,"");//Error
复制代码

\quad首先咱们知道,定义了一个键值对类型都是String类型的HashMap,若是你往里面丢入一个类型为非String类型的键值对,那么确定是会报错的。可是若是把尖括号及里面的内容去掉的话,程序是能够经过编译的。bash

HashMap hashMap = new HashMap<>();
       hashMap.put(1,"");//Pass
复制代码

那么你会问,何苦要去掉括号呢?我明确的知道键值对的类型不是很方便嘛。确实,明确类型以后,编译器能够帮咱们检查出不少插入数据类型不正确的问题,可是要知道泛型是在JDK1.5才出现的,那么在那个没有泛型的年代,是怎么确保类型安全的呢?ide

1.在没有泛型的年代,如何确保类型安全?

\quad要知道,大多数的语言在最开始的时候都是没有泛型的例如C#,Go(如今都不支持),Java。就拿前面例子中的hashMap来讲,在JDK1.5以前,能够在其中放入任何类型的数值。可是问题来了,若是咱们想构造一个只能传入<String,String>类型的HashMap要怎么作呢?能够用相似装饰器模式来实现,见代码:ui

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);
    }
}
复制代码

\quad可是问题来了,对于茫茫可能发生的状况,按照这样的话,我岂不是须要每一种都列出对应的状态。因此在JDK1.5中推出了泛型,简化了这些麻烦的操做,可是Java为了向后兼容性,因此这种泛型只是在编译期间存在的,运行期间仍是会经过类型擦除(Type Erasure),去掉泛型的。这一点能够经过看字节码能够看出: spa

\quad 那么问题来了,ArrayList<Integer>与ArrayList<Object>是同一个类嘛?
问题的答案是:是也不是,为何这么说呢?前面提到,在运行期间通过类型擦除以后,他们都回归为ArrayList。说不是的缘由在于他们之间不能相互赋值:
同时注意一点,ArrayList<String>并非ArrayList<Object>的子类型,也是因为泛型擦除:

public static void main(String[] args) {
       foo(new ArrayList<String>());//Error

    }
    public static void foo(ArrayList<Object> objects){
        //do something
    }
复制代码

2.泛型的绑定

\quad 下面经过几个例子来引出泛型的使用:
code

  • 1.extends -限定泛型必须是自己或者是其子类(上边界)

\quad 好比如今要比较两个参数的大小,可是参数的类型不肯定,那么就能够用到泛型的绑定了。
举例说明:cdn

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;
    }
复制代码

结果以下:blog

2
2.0
2.0
2
abd

Process finished with exit code 0
复制代码

可是在这就有疑问了:
\quad 1.为何Comparable明明是个接口,却要用extends关键字?
\quad 2.若是对两种都实现了Comparable接口,可是对两者类型不一样的参数进行比较,会有什么结果呢?

回答:
\quad 1.在泛型中,关键字extends后面既能够跟类,也能够跟接口。可是在实际意义上更接近因而绑定类型的自身或者子类型,因此用extends关键字更加接近。
\quad 2.见代码: 继承

在编译期间就会被检查出错误,可是有种办法能够绕过这种检查:
可是运行一样报错:

究其缘由的话,能够看字节码:
INVOKEINTERFACE-调用接口方法,经过前面的错误提示能够看到,在这里多态调用了String的compareTo方法,因此只能传入一个String类型的参数。这里咱们传入了一个int,在类加载的验证阶段,就会找出这个错误。

  • 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;
    }
}
复制代码
相关文章
相关标签/搜索