范型只在编译阶段有效
ArrayList<String> a = new ArrayList<String>();
ArrayList b = new ArrayList();
Class c1 = a.getClass();
Class c2 = b.getClass();
System.out.println(c1 == c2); // true
输出结果为true,证明了编译之后,程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。
在编译过程中,正确的检验出泛型结果后,会将范型的相关信息擦除。也就是说,成功编译后的class文件中是不包含任何泛型信息的。
ArrayList<String> a = new ArrayList<>();
a.add("CSDN_SEU_Calvin");
Class c = a.getClass();
try {
Method method = c.getMethod("add", Object.class);
method.invoke(a, 100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(a); // [CSDN_SEU_Calvin, 100]
上述代码绕过了编译阶段,也绕过了反射。
泛型类和泛型方法
使用泛型:
/*
* 使用泛型
*/
public class FX<T> {
private T ob;// 定义泛型成员变量
public FX(T ob) {
this.ob = ob;
}
public T getOb() {
return ob;
}
public void showType() {
System.out.println("T的实际类型是:" + ob.getClass().getName());
}
public static void main(String[] args) {
FX<Integer> intOb = new FX<Integer>(100);
intOb.showType();
System.out.println("value=" + intOb.getOb());
System.out.println("--------------------------");
FX<String> strOb = new FX<String>("CSDN_SEU_Calvin");
strOb.showType();
System.out.println("value=" + strOb.getOb());
}
}
// T的实际类型是:java.lang.Integer
// value=100
// --------------------------
// T的实际类型是:java.lang.String
// value=CSDN_SEU_Calvin
不使用泛型:
public class FX {
private Object ob;
public FX(Object ob) {
this.ob = ob;
}
public Object getOb() {
return ob;
}
public void showType() {
System.out.println("T的实际类型是:" + ob.getClass().getName());
}
public static void main(String[] args) {
FX intOb = new FX(100);
intOb.showType();
System.out.println("value=" + intOb.getOb());
System.out.println("--------------------------");
FX strOb = new FX("CSDN_SEU_Calvin");
strOb.showType();
System.out.println("value=" + strOb.getOb());
}
}
// T的实际类型是:java.lang.Integer
// value=100
// --------------------------
// T的实际类型是:java.lang.String
// value=CSDN_SEU_Calvin
通配符
为了引出通配符的概念,先看以下代码:
List<Integer> ex_int = new ArrayList<>();
List<Number> ext_num = ex_int; // 出现编译错误
出现错误的原因是,虽然Integer是Number的子类,但是List<Integer>不是List<Number>的子类。如果可以,那么也可以使用ext_num.add(newDoubt())在一个List中装入各种不同类型的子类,这显然是不可以的,因为在取出的时候,是无法区分取出的数据是Integer还是Double类型的。
因此,需要一个在逻辑上可以用来同时表示为List<Integer>和List<Number>的父类的一个引用类型,因此就出现了类型通配符。在以下例子中表示为FX<?>。
public class Genericity {
public static class FX<T> {
private T ob;
public FX(T ob) {
this.ob = ob;
}
}
public static void main(String[] args) {
FX<Number> ex_num = new FX<Number>(100);
FX<Integer> ex_int = new FX<>(200);
getData(ex_num);
getData(ex_int);// 使用getData(FX<Number> temp)方法,编译出错
}
// public static void getData(FX<Number> temp) {
// System.out.println("temp的实际类型是:" + temp.ob);
// }
public static void getData(FX<?> temp) {
System.out.println("temp的实际类型是:" + temp.ob);
}
}
//temp的实际类型是:100
//temp的实际类型是:200
泛型的好处
**类型安全:**通过泛型定义的变量类型限制,编译器可以更有效的提高Java程序的类型安全。
**消除强制类型转换:**使得代码更加可读,并减少出错机会。所有的强制转换都是自动和隐式的。
List和List<?>
List实际上也是List<Object>。List实际上表示持有任何Object类型的原生List。- 而
List<?>表示具有某种特定类型的List,只是我们不知道那种类型是什么。