Java泛型这个特性是从JDK 1.5 才开始加入的,因此为了兼容之前的版本,Java泛型的实现采取了“伪泛型”的策略,即Java在语法上支持泛型,但是再殡仪截断会进行所谓的“类型擦除”(Type Erasure),将所有的泛型表示(尖括号中的内容)都替换为具体的类型(其对应的原生态类型),就像完全没有泛型一样。
为什么会引入泛型
泛型的本质是为了参数化类型(在不创建新的类型的情况下,通过泛型指定的不同类型来控制形参具体限制的类型)。也就是在泛型的使用过程中,操作的数据类型被指定为一个参数,这种参数类型可以用在类、接口和方法中,分别被称谓泛型类、泛型接口、泛型方法。
引入泛型的意义:
- 适用于多种数据类型执行相同的代码(代码复用)
当没有泛型时,想要实现不同类型的加法,就需要对每种类型都要重载一个add方法如下:
public class GenericsExampleReuse {
private static int add(int a, int b) {
System.out.println(a + "+" + b + "=" + (a+b));
return a + b;
}
private static float add(float a, float b) {
System.out.println(a + "+" + b + "=" + (a+b));
return a + b;
}
private static double add(double a, double b) {
System.out.println(a + "+" + b + "=" + (a+b));
return a + b;
}
}
如果使用泛型,可以复用为同一个方法:
public class GenericsExampleReuse {
private static <T extends Number> double add(T a, T b) {
System.out.println(a + "+" + b + "=" + (a.doubleValue()+b.doubleValue()));
return a.doubleValue() + b.doubleValue();
}
}
- 泛型中的类型在使用时指定,不需要强制类型转化(类型安全,编译器会检查类型)
如下示例: list 中的元素都是Object类型(无法约束其中的类型),所以在取出集合元素时需要人为的强制类型转化到具体的目标类型,且很容易出现 java.lang.ClassCastException
异常。
List list = new ArrayList();
list.add("xxString");
list.add(100d);
list.add(new Person());
引入泛型,可以提供类型约束,提供编译前的检查
泛型的基本使用
通过一些例子学习泛型的使用;泛型有三种使用方式,分别是:泛型类、泛型接口、泛型方法。参考《李兴华 - Java实战经典》
泛型类
定义泛型方法时,必须在返回值前加一个
Class
为什么要用变量c 来创建对象?泛型方法中无法得知具体的类型,也不知道构造方法,所以无法通过new 来创建对象,因此利用变量c 的newInstance 方法去创建对象,即利用反射创建对象。
泛型方法要求参数是Class
为什么要使用泛型方法呢?因为泛型类要在实例化的时候就指明类型,如果想换一种类型,不得不重新new一次,可能不够灵活;而泛型方法可以在调用的时候指明类型,更加灵活。
泛型的上下限
- 先看下如下的代码,很明显是会报错的 (具体错误原因请参考后文)。 ```java class A{} class B extends A {}
// 如下两个方法不会报错
public static void funA(A a) {
// …
}
public static void funB(B b) {
funA(b);
// …
}
// 如下funD方法会报错
public static void funC(List listA) {
// …
}
public static void funD(List listB) {
funC(listB); // Unresolved compilation problem: The method doPrint(List) in the type test is not applicable for the arguments (List)
// …
}
```
那么如何解决?
为了解决泛型中隐含的转换问题,Java泛型引入了类型参数的上下边界机制。<? extends A> 表示该类型参数可以是A(上边界)或者A的子类类型。编译时擦除到类型A,即用A类型代替类型参数。这种方法可以解决开始