在工作中经常会使用到泛型,所以整理一些泛型相关的资料,一起理解,学习一下。
1.泛型是什么,解决了什么问题。
泛型又叫参数化类型,就是将类型像参数一样传递,可以在类,接口,方法中使用。在调用时,传递具体的类型。它可以提高代码的可重用性,让我们可以方便的实现一些通用算法,还可以提供更强大的类型检查,在编译阶段就可以发现代码的安全性错误,相比运行时才发现错误,可以让我们更好的定位,修复错误。
2.泛型的三种使用方式
2.1 泛型类
泛型类通过在类名后加上一对尖括号 <T,T...>来定义,下面看代码:
  class ClassName<T>{/* ... */}
括号中的内容,就是指定的类型参数。通常类型参数名称是单个大写字母。最常用的类型参数名称是:
- E - 元素(由 Java 集合框架广泛使用)
- K - Key
- N - Number
- T - Type
- V - Value
- S,U,V etc. - 2nd, 3rd, 4th types
下面是一个最简单的泛型类:
public class Generics<T> {
    private T t; /* 泛型成员变量 */
    /* 泛型成员方法(无返回值) */
    public void set(T t) {
        this.t = t;
    }
    /* 泛型成员方法(有返回值) */
    public T get() {
        return t;
    }
}
上面Generics类中的<T>就是要传入的参数,下面看看泛型类是怎样调用/实例化的。
Generics<Integer> gInt = new Generics<Integer>();
上面例子中的Integer类型,就像参数一样传递给了Generics类。
需要注意的是,在泛型类中,不可以声明类型为,类型参数的静态字段:
public class Generics<T> {
    private static T t; /* error 编译时会报错*/
}
如果Java允许这样声明,那么我们看看下面的例子:
Generics<Integer> gInt = new Generics<Integer>();
Generics<String> gString = new Generics<String>();
Generics<Double> gDouble= new Generics<Double>();
因为静态字段会被以上三个对象共享,所以它的类型是什么呢?因此,不可以声明类型为,类型参数的静态字段。
2.1.1 原生类型(raw type)
原生类型就是一个泛型类在我们在使用时,没有传递类型参数。下面看一个例子:
public class Generics<T> {
    private T t; /* 泛型成员变量 */
}
我们正常实例化这个类时会传递类型参数:
Generics<Integer> gInt = new Generics<Integer>();
下面是原生类型,调用时不传递参数类型:
Generics g = new Generics();
原生类型是Java在推出了泛型后,为了兼容过去的代码而产生的。所以Java为了向后兼容,允许将参数化类型分配给它的原生类型:
Generics<Integer> gInt = new Generics<Integer>(); /* 参数化类型*/
Generics g = gInt; /* 原生类型*/
如果将原生类型分配给参数化类型会收到一个警告:
Generics g = new Generics(); /* 原生类型*/
Generics<Integer> gInt = g; /* 警告:未经检查的类型转换 */
如果使用原生类型去调用对应泛型中的方法只会得到一个警告:
Generics<Integer> gInt = new Generics<Integer>();
Generics g = gInt;
g.set("a");
这种调用方式会绕过泛型的类型检查,代码的安全性错误会在运行时才能发现。所以尽量不要使用原生类型。