泛型的定义和意义
泛型的本质是参数化类型,提供了编译时的安全检测机制,允许程序在编译时检测非法的类型。
使用泛型的好处就是:
① 代码复用,多种数据类型执行相同的代码;
② 在编译期就能够检查类型是否安全(只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常),同时所有强制性类型转换都是自动和隐式进行的,提高了代码的安全性和重用性。
Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉,这也就是通常所说类型擦除
一些常用的泛型标记:
泛型标记 | 说明 |
---|---|
T(Type) | 表示java的类型,包括基本的类和开发者自定义的类 ,比如T Object |
E(Element) | 表示在集合中存放的元素,是在集合中使用的 ,比如List< E > list |
K(Key) | 表示键,在存储键值对的数据结构中使用,比如map中的key |
V(Value) | 表示值,和K对应 |
N(Number) | 表示各种数值类型 |
? | 表示不确定的java类型 |
泛型的使用
泛型类
泛型类就是把泛型定义在类上,只有在使用该类的时候,才把类型明确下来。
/*
把泛型定义在类上,类型变量定义在类上,方法中也可以使用
*/
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
测试
public static void main(String[] args) {
//创建对象并指定元素类型 String类型
ObjectTool<String> tool = new ObjectTool<>();
tool.setObj(new String("aaa"));
String s = tool.getObj();
System.out.println(s);
//创建对象并指定元素类型 Integer类型
ObjectTool<Integer> objectTool = new ObjectTool<>();
/**
* 如果我在这个对象里传入的是String类型的,它在编译时就不会通过
*/
objectTool.setObj(1);
int i = objectTool.getObj();
System.out.println(i);
}
泛型方法
如果是仅仅需要某个方法使用泛型,那定义在类上就有点小题大做了,单独给某个方法定义泛型,其他地方调用时就无需关注方法的返回类型。
//定义泛型方法..
public <T> void show(T t) {
System.out.println(t);
}
测试
public static void main(String[] args) {
//创建对象
ObjectTool tool = new ObjectTool();
//调用方法,传入的参数是什么类型,返回值就是什么类型
tool.show("hello");
tool.show(2);
tool.show(2.11);
}
泛型接口
/*
把泛型定义在接口上
*/
public interface Inter<T> {
public abstract void show(T t);
}
子类明确泛型类的类型参数变量
/**
实现接口,重写方法
* 子类明确泛型类的类型参数变量:String
*/
public class InterImpl implements Inter<String> {
@Override
public void show(String s) {
System.out.println(s);
}
}
测试
//这时候只能是String
Inter<String> a = new InterImpl();
i.show("hello");
- 子类不明确泛型类的类型参数变量:
/**
* 子类不明确泛型类的类型参数变量:
* 实现类也要定义出<T>类型的
*/
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
测试
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
//这个时候可以是String可以是Integer等等
Inter<String> i1 = new InterImpl<>();
i2.show("11");
Inter<Integer> i2 = new InterImpl<>();
i2.show(11);
限定泛型类型变量
- 对泛型上限的限定
在Java中使用通配符“? ”和“extends”关键字指定泛型的上限,具体用法为<? extends T>,它表示该通配符所代表的类型是T类的子类或者接口T的子接口。
- 对泛型下限的限定
在Java中使用通配符“? ”和“super”关键字指定泛型的下限,具体用法为<? super T>,它表示该通配符所代表的类型是T类型的父类或者父接口。
类型擦除
在编码阶段采用泛型时加上的类型参数,会被编译器在编译时去掉,这个过程就被称为类型擦除。因此,泛型主要用于编译阶段。在编译后生成的Java字节代码文件中不包含泛型中的类型信息。例如,编码时定义的List和List在经过编译后统一为List。JVM所读取的只是List,由泛型附加的类型信息对JVM来说是不可见的。Java类型的擦除过程为:首先,查找用来替换类型参数的具体类(该具体类一般为Object),如果指定了类型参数的上界,则以该上界作为替换时的具体类;然后,把代码中的类型参数都替换为具体的类。