一、关于泛型
泛型程序设计(generic programming):意味着编写地代码可以对多种不同类型地对象重用。
泛型是一个表达能力很强地类型系统,允许设计者详细地描述变量和方法的类型如何变化。
1.1 为什么要使用泛型?
- 在泛型类出现之前,程序员必须使用
Object编写适用于多种类型地代码,这很烦琐,也很不安全。
1.2 如何使用泛型:
- 类型参数
public static void main(String[] args) {
int num = 0;
String str = "";
// 定义类型参数为String的集合对象。
List<String> list = new ArrayList<>();
// 编译器可以检查,防止你插入错误的对象。
list.add(num); // 报错,禁止添加该类型变量。
list.add(str);
}
- 定义泛型类
/* *
* 备注:常见的做法是类型变量使用大写字符。
* Java库:使用变量E表示集合元素类型,K和V表示键和值的类型,T表示“任意类型”。
*
* 则此处通过通配符<T>,定义泛型类。
* 有setter,getter方法。
*/
public class Car<T> {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
// 通过通配符定义的Car类,更加灵活
Car<Electric> electricCar = new Car<>();
electricCar.setName("电动类型");
Car<Fuel> fuelCar = new Car<>();
fuelCar.setName("燃油类型");
System.out.println(electricCar.getName());
// print 电动类型
System.out.println(fuelCar.getName());
// print 燃油类型
}
- 定义泛型方法
/* *
* 通过通配符,定义一个泛型方法。
* 类型参数:<T>
* 返回值类型:T
* 参数列表:T filed
*/
private static <T> T getFiledValue(T filed) {
return filed;
}
public static void main(String[] args) {
// 不同类型的变量
int num = 0;
String str = "str";
System.out.println(getFiledValue(num));
// print 0
System.out.println(getFiledValue(str));
// print "str"
}
- 类型变量的限定
/* *
* 通过extends关键字,对类型参数进行限定。
* 一个类型变量或通配符可以有多个限定。
* extends限定可以有多个接口,用"&"分隔,但只能有一个类,且必须是排在第一位。
*/
private static <T extends Integer> T getFiledValue(T filed) {
return filed;
}
public static void main(String[] args) {
// 不同类型的变量
Integer num = 0;
String str = "str";
System.out.println(getFiledValue(num));
System.out.println(getFiledValue(str)); // 类型不符,编译报错。
}
- 通配符限定拓展
// 带有[子类型限定]的通配符,允许你[读取]一个泛型对象。
List<? extends T>
// 带有[超类型限定]的通配符,允许你[写入]一个泛型对象。
List<? super T>
二、泛型代码与虚拟机
虚拟机没有泛型类型对象,所有对象都属于普通类。
2.1 类型擦除:
-
无论何时定义一个泛型类型,都会自动提供一个相应的原始类型。
-
类型变量会被擦除,替换为其限定类型(或者,对于无限定的变量则替换为
Object)。
2.2 需要记住:
虚拟机中没有泛型,只有普通的类和方法。
所有的类型参数都会替换为它们的限定类型。
会合成
桥方法来保持多态。为保持类型安全性,必要时会插入强制类型转换。
三、限制与局限
// 注意:以下均为【错误示例】!!!
// 1. 不能用基本类型作为类型参数!
List<int> list = new ArrayList<>();
// 2. 运行时类型查询只适用于原始类型!
if (car instanceof Car<T>) {
}
// 3. 不能创建参数化类型的数组!
int[] ints = new int <T>[10];
// 4. 不能构造泛型数组!
T[] array = new T[10];
// 5. 不能在静态字段或方法中引用类型变量!
private static T filed;
private static T getfiled(){
return filed;
}
// 6. 不能抛出也不能捕获泛型类对象!
try {
// do something ...
} catch (T e) {
System.out.println("...");
}
// 7. 当类型擦除后,不允许创建引发冲突的条件!
// 譬如:当前类有equals()方法,类型被擦除后被当作Object类型,则调用的是Object.equals()方法。
四、结束语
“-------怕什么真理无穷,进一寸有一寸的欢喜。”
微信公众号搜索:饺子泡牛奶。