一.泛型的概念
Java泛型(Generics)是JDK5引入的重要特性,它允许在定义类、接口、方法时使用类型参数,从而实现代码的类型安全和复用(使代码能够适用于多种数据类型)
泛型的机制可以让程序员在编译的时候就检测到非法的类型数据结构(与泛型指定的类型不一样,在编译阶段就会报错)
泛型的本质就是参数化类型,也就是所操作的数据类型被指定为一个参数
二.泛型产生的背景
java
package fanxing;
import java.util.ArrayList;
// JDK5以前的写法:没有泛型
public class Background {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("java");
list.add(100);
list.add(true);
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);// ctrl alt v 自动生成变量,变量的类型自动会写为Object
//System.out.println(o); // 这样直接输出是没有什么问题
// 但是有时候我们可能因为某些需求需要转换类型,就会发生类型转换错误
// 比如:
String s = (String)o;
System.out.println(s); // ClassCastException
}
}
}
Java推出泛型以前,程序员可以构建一个元素为Object类型的集合,该集合可以存储任意类型的对象。而在适用集合的过程中,需要程序员明确知道存储的元素的类型,否则很容易引发ClassCastException异常
java
package fanxing;
import java.util.ArrayList;
// JDK5及以后版本的写法:可以使用泛型
public class Background {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>(); // 使用了泛型来创建集合,集合中只能存储String对象
list.add("java");
list.add("100");
list.add("true");
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
System.out.println(s);
}
}
}
成功输出,没有报错
java
public class Background {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>(); // 使用了泛型来创建集合,集合中只能存储Integer对象
list.add(100);
list.add(200);
list.add(300);
int sum = 0;
for (int i = 0; i < list.size(); i++) {
Integer num = list.get(i);
sum += num; // JDK5以及以后的版本,都引入了自动装箱插箱机制
// 自动装箱就是把基本类型的变量转换成对应的包装类对象,如int 转换成 Integer对象
// 自动拆箱就是把对应的包装类对象转换成对应的基本类型变量,如Integer对象转换成int
// 这里的加法就是发生了自动插箱机制
}
System.out.println(sum);
}
}
java
public class Background {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>(); // 使用了泛型来创建集合,集合中只能存储Integer对象
list.add(100);
list.add(200);
list.add(300);
int sum = 0;
for (int i = 0; i < list.size(); i++) {
Integer num = list.get(i);
//int intNum = (int) num; // 手动拆箱,Java规定包装类转基本类型时,调用对应的 xxxValue() 方法
int intNum = num.intValue(); // 效果与上一行一样
sum += intNum;
}
System.out.println(sum);
}
}
三.泛型类
Java泛型使用特定的泛型标识符(类型参数)来代表未知的类型,虽然理论上可以用任意标识符,但社区形成了一套约定俗成的命名规范
Java官方推荐的类型参数标识符:
| 标识符 | 英文全称 | 含义 | 典型使用场景 |
|---|---|---|---|
T |
Type | 类型 | 通用类型参数(最常用) |
E |
Element | 元素 | 集合类中的元素类型 |
K |
Key | 键 | Map中的键类型 |
V |
Value | 值 | Map中的值类型 |
N |
Number | 数字 | 数值类型 |
R |
Return | 返回值 | 方法的返回类型 |
S, U, V |
- | 第二、三、四个类型 | 多个类型参数时依次使用 |
java
package fanxing;
/**
* 泛型类的定义
* @param <T> 泛型标识,相当与类型参数,创建对象的时候会自动替换为具体的类型
*/
public class GenericClass<T> {
private T value;
public GenericClass(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public void setValue(T value) {
this.value = value;
}
public static void main(String[] args) {
/*泛型类的使用方式:
* 类名<具体的数据类型> 对象名 = new 类名<具体的数据类型>();
* Java1.7之后,后面的<>中的具体数据类型可以省略不写,如:
* 类名<具体的数据类型> 对象名 = new 类名<>();*/
// 在创建泛型类的对象时,要确定泛型类中具体的数据类型
GenericClass<String> gc = new GenericClass<>("Java"); // 确定泛型类的数据类型为String,则传参就只能是String类型
String value1 = gc.getValue(); // ctrl alt v自动生成变量声明,String对应了在创建对象的泛型参数
System.out.println(value1);
GenericClass gc2 = new GenericClass(123); // 如果在创建泛型类对象的时候没有指定具体的类型,则可以传递Object类型对象
Object value2 = gc2.getValue();
System.out.println(value2);
//GenericClass<int> gc3 = new GenericClass<> (123); // 泛型参数不支持基本数据类型,需要使用包装类
}
}
泛型类派生子类
1.若子类也是泛型类,则子类的泛型类型和父类的泛型类型要一致
java
package fanxing;
public class Parent<E> {
E e;
//
// public Parent(E e) {
// this.e = e;
// }
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
}
java
package fanxing;
public class ChildFirst<T> extends Parent<T> {
@Override
public T getE() {
return super.getE();
}
}
java
package fanxing;
public class Test1 {
public static void main(String[] args) {
ChildFirst<String> cf = new ChildFirst<>();
cf.setE("Hello World");
String e = cf.getE();
System.out.println(e); // Hello World
}
}
2.若子类不是泛型类,则父类要明确泛型的数据类型
java
package fanxing;
public class ChildSecond extends Parent<String>{
@Override
public String getE() {
return super.getE();
}
}
java
package fanxing;
public class Test1 {
public static void main(String[] args) {
ChildFirst<String> cf = new ChildFirst<>();
cf.setE("Hello World");
String e = cf.getE();
System.out.println(e); // Hello World
ChildSecond cs = new ChildSecond();
cs.setE("Hello Java");
String e1 = cs.getE();
System.out.println(e1); // Hello Java
}
}