泛型
介绍
-
泛型是JDK 5 中引入的新特性
- 提供编译时类型安全检测机制
- 允许程序员在编译时检测到非法类型
-
本质:参数化类型
- 所操作的数据类型被指定为一个参数
-
泛型是一种类似模板代码的技术,不同语言的泛型实现方式不一定相同
-
Java
的泛型实现方式是擦拭法(Type Erasure) -
擦拭法指:虚拟机对泛型其实一无所知,所有的工作都是编译器做的
-
Java泛型
Java
的泛型是由编译器在编译时实行的
- 编译器内部永远把所有类型
T
视为Object
处理- 但需要转型的时候,编译器会根据
T
的类型自动实行安全地强制转型
- 但需要转型的时候,编译器会根据
- 局限
<T>
不能是基本类型,例如int
- 因为实际类型是
Object
,Object
类型无法持有基本类型
- 因为实际类型是
- 无法取得带泛型的
Class
- 所有泛型实例无论
T
的类型getClass()
返回同一个Class
实例 - 因为编译后全部都是
ClassName<Object>
- 所有泛型实例无论
- 无法判断带泛型的类型
- 并不存在
ClassaName<String>.class
,而是只有唯一的ClassName.class
x instanceof Pair<String>
不能判断
- 并不存在
- 不能实例化
T
类型- 例如:
new T()
- 例如:
- 子类可以获取父类的泛型类型
<T>
通配符
?
- 类型通配符一般是使用
?
代替具体的类型参数- 例如:
List<?>
在逻辑上是List<String>
、List<Integer>
等所有List<具体类型实参>
的父类
- 例如:
有界类型
-
限制类型参数的类型种类范围
- 如:只接受
Number
或Number
子类的实例;就是有界类型参数的目的
- 如:只接受
-
声明一个有界的类型参数
<>
使用通配符?
- 跟
extends
、super
关键字extends
表示该类型的子类型参数supre
表示该类型的父类型参数
- 紧跟其上、下界
<? extends T>
<? extends T>
:通配符代表的类型是 T 类型的子类
- 规定上限类型
T
<? super T>
<? super T>
:通配符代表的类型是T
类型的父类- 规定下限类型
T
- 规定下限类型
示例
public class MaximumTest{
// 比较三个值并返回最大值
public static <T extends Comparable<T>> T maximum(T x, T y, T z){ // 定义泛型方法,返回值是 Comparable 类型子类
T max = x; // 假设x是初始最大值
if ( y.compareTo( max ) > 0 ){
max = y; // y 更大
}
if ( z.compareTo( max ) > 0 ){
max = z; // 现在 z 更大
}
return max; // 返回最大对象
}
public static void main( String args[] ){
System.out.println(maximum( 3, 4, 5 )); // 5
System.out.println(maximum( 6.6, 8.8, 7.7 )); // 8.8
System.out.println(maximum( "pear", "apple", "orange" )); // pear
// 在传入参数后 int、double类型数据自动装箱为包装类型;Integer、Double、String 类型都实现了 Comparable 接口
}
}
泛型方法
-
调用时可接收不同类型参数
- 根据传递给泛型方法的参数类型,编译器适当的处理每一个方法调用
- 要防止重复定义方法
- 例如:
public boolean equals(T obj)
; - 错误重写
equals
方法
- 例如:
-
声明
- 类型参数部分,使用尖括号分隔,在返回类型值之前
- 例如:
<E>
- 例如:
- 包含一个或多个类型参数,用逗号隔开
- 一个泛型参数被称为一个类型变量,用于指定一个泛型类型名称的标识符
- 例如:
<E, E>
- 例如:
- 一个泛型参数被称为一个类型变量,用于指定一个泛型类型名称的标识符
- 泛型方法的声明的类型参数只能代表引用类型,不能是原始类型
- 如:int、double、char 等 需要使用包装类型
- 类型参数部分,使用尖括号分隔,在返回类型值之前
-
类型参数能被用来声明返回值类型
- 且能作为泛型方法得到的实际参数类型的占位符
-
类型参数使用大写形式且比较短,一般一个字母
-
Java
库中使用变量E
表示集合的元素类型 -
K
和V
表示键与值的类型- 比如:
Map
的键与值
- 比如:
-
通常使用
T
或U
、S
表示“任意类型”
-
-
public class Test{ // 泛型方法 printArray public static <E> void printArray( E[] inputArray ){ // 定义泛型 <E>,使 E[] 可以接收任意类型数据定义数组 for ( E element : inputArray ){ System.out.printf( "%s ", element ); // 遍历输出数组元素 } } public static void main( String args[] ){ // 创建不同类型数组: Integer, Double 和 Character Integer[] intArray = { 1, 2, 3, 4, 5 }; Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 }; Character[] charArray = { 'H', 'E', 'L', 'L', 'O' }; printArray(intArray); // 传递整型数组:1 2 3 4 5 printArray( doubleArray ); // 传递双精度型数组:1.1 2.2 3.3 4.4 printArray( charArray ); // 传递字符型数组:H E L L O } }
泛型类
定义
-
类名后面添加类型参数声明部分
-
包含一个或多个类型参数,参数间用逗号隔
-
泛型参数被称为类型变量,用于指定一个泛型类型名称的标识符
- 因为接受一或多个参数,这些类被称为参数化的类或参数化的类型
-
实际数据类型在类被实例化的时候声明
-
public class Box<T> { // 泛型类 private T t; // 泛型属性 public void add(T t) { // 泛型方法 this.t = t; } public T get() { return t; } public static void main(String[] args) { Box<Integer> integerBox = new Box<Integer>(); // 实例化对象声明泛型为 Integer 类型 Box<String> stringBox = new Box<String>(); // 实例化对象声明泛型为 String 类型 integerBox.add(10); stringBox.add("菜鸟教程"); System.out.println("整型值为:" + integerBox.get()); // 整型值为:10 System.out.println("字符串为:" + stringBox.get()); // 字符串为:菜鸟教程 } }
继承
-
一个类可以继承自一个泛型类
- 例如:父类
Pair<Integer>
,子类是IntPair
public class IntPair extends Pair<Integer> {}
- 继承泛型类,已经声明泛型类型
- 子类
IntPair
并没有泛型类型,正常使用即可
- 例如:父类
-
无法获取
Pair<T>
的T
类型- 即给定变量
Pair<Integer> p
,无法从p
中获取到Integer
类型
- 即给定变量
-
但父类是泛型类型的情况下,编译器必须把类型
T
保存到子类的class
文件- 不然编译器就不知道
IntPair
只能存取Integer
类型- 对
IntPair
T
是Integer
类型
- 对
- 不然编译器就不知道
-
在继承了泛型类型的情况下,子类可以获取父类的泛型类型
-
例如:
IntPair
可以获取到父类的泛型类型Integer
-
获取父类的泛型类型代码比较复杂
-
import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; public class Main { // 获取父类泛型类型 public static void main(String[] args) { Class<IntPair> clazz = IntPair.class; Type t = clazz.getGenericSuperclass(); if (t instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) t; Type[] types = pt.getActualTypeArguments(); // 可能有多个泛型类型 Type firstType = types[0]; // 取第一个泛型类型 Class<?> typeClass = (Class<?>) firstType; System.out.println(typeClass); // Integer } } } class Pair<T> { private T first; private T last; public Pair(T first, T last) { this.first = first; this.last = last; } public T getFirst() { return first; } public T getLast() { return last; } } class IntPair extends Pair<Integer> { public IntPair(Integer first, Integer last) { super(first, last); } }
-