目录
泛型的定义
泛型,即“参数化类型”。就是将程序需要的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后在使用/调用时传入具体的类型(类型实参)。
一些常用的泛型类型变量:
E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)
泛型分类
泛型分为“泛型类”、“泛型接口”和“泛型方法”。
泛型类:
public class GenericClass<T> {
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
泛型接口:
public interface GenericIntercace<T> {
T getData();
}
泛型方法:
private static <T> T genericAdd(T a, T b) {
System.out.println(a + "+" + b + "="+a+b);
return a;
}
限定泛型类型变量
Java中的限定通配符包括:
<? extends Parent>
指定了泛型类型的上界,即传入的类型必须是Parent的子类或实现类。<? super Child>
指定了泛型类型的下界,即传入的类型必须是Child的父类。<?>或<T等符号>
表示没有限制的泛型类型。
如:public class Test<T extends Number> {},public <T super Integer> T add(T num1 ,T num2){}。
泛型的意义
- 能够复用代码。
- 编译期间类型检查,避免类型转换异常。
复用代码。我们以List集合举例,若我们要自己实现一个List集合,那么可能首先想到的是创建多个List类,这样我们为了做到可以在List中放入任何类型的数据,我们必须定义相同数量的List类,如:
class List1{
Integer[] data;
void add(Integer p);
Integer get(int index);
}
class List2{
Float[] data;
void add(Float p);
Float get(int index);
}
......
类型安全。在上面的基础上,有一种方法可以避免大量重复代码,那就是Object[] data。这样我们可以传入任何对象,但另一个问题来了,存数据的时候很nice,但是取数据的时候返回的也是Object对象,这样我们就必须进行类型强转,这样很有可能出现类型转化异常。
class List{
Object[] data;
void add(Object p);
Object get(int index);
}
List list = new List();
list.add(10);
list.add("hello");
Integer v1 = (Integer)list.get(0);
Stringv1 = (String)list.get(1);
显然,使用泛型,就很好的避免了上面两个问题:
List<String> list = new ArrayList<>();
list.add("hello");
String value = list.get(0);
List<Integer> list = new ArrayList<>();
list.add(1);
Integervalue = list.get(0);
泛型的原理
Java语言中的泛型只在源码中存在,在编译后的字节码文件中,就已经替换为原来的原生类型了,并且在相应的地方插入了强制转型代码(被称作“泛型擦除”)。因此,对于运行期的Java语言来说,ArrayList<Integer>与ArrayList<String>就是同一个类。需要注意的是,Java中泛型在运行期是不可见的,会被擦除为它的上级类型。如果是没有限定的泛型参数类型,就会被替换为Object。
下面是一个Java泛型的例子,是泛型擦除前的代码:
public static void main(String[] args) {
Map<String,String> map = new HashMap<>();
map.put("hello","你好");
map.put("how are you?","吃了没?");
System.out.println(map.get("hello"));
System.out.println(map.get("how are you?"));
}
将上面代码编译成Class文件,然后再用字节码反编译工具进行反编译后,将会发现泛型都不见了,程序又变回了Java泛型出现之前的写法,泛型类型都变回了原生类型,如下:
public static void main(String[] args) {
Map<Object,Object> map = new HashMap<>();
map.put("hello","你好");
map.put("how are you?","吃了没?");
System.out.println((String)map.get("hello"));
System.out.println((String)map.get("how are you?"));
}
泛型中的约束和局限性
- 不能实例化泛型类
- 静态变量或方法不能引用泛型类型变量,但是静态泛型方法是可以的
- 基本类型无法作为泛型类型
- 无法使用instanceof关键字或==判断泛型类的类型
- 泛型类的原生类型与所传递的泛型无关,无论传递什么类型,原生类是一样的
- 泛型数组可以声明但无法实例化
- 泛型类不能继承Exception或者Throwable
- 不能捕获泛型类型限定的异常但可以将泛型限定的异常抛出
参考:https://www.jianshu.com/p/986f732ed2f1,《深入理解Java虚拟机》,https://www.cnblogs.com/huansky/p/8043149.html