泛化的 Class 引用
我們由一段代碼來切入今天的主題:
public class Demo {
public static void main(String[] args) {
try{
Class<? extends Number> numClass = int.class;
numClass = double.class;
numClass = Integer.class;
} catch(Exception e){
e.printStackTrace();
}
}
}
上述代碼沒有任何的實際意義,但是能引出以下兩個問題:
1)Class 是什么類?
2)泛型有什么作用?
對于這兩個問題,我們的解答是:
-
Class 的功能與使用
-
Class類:Class引用總是指向某個類的Class對象。Class類可以用于制造類的實例,并包含可作用于這些實例的所有方法代碼,并且包含該類的靜態(tài)成員。 -
Class<?>與Class是等效的,但是使用Class<?>的好處是表示你并不是由于疏忽而未指定具體的類引用,而是選擇了非具體的版本。
-
-
使用帶泛型的 Class 引用的好處:
- 使用
Class<?>調(diào)用getInstanse()方法獲取到的對象是 Object 類型的實例。但如果使用Class<Integer>則是 Integer 類型的。
- 使用
所以 Class 類包含了其他類的類型信息??捎糜谥圃祛惖膶嵗筒榭搭惖某蓡T和方法。
泛型通配符 extends 和 super
- 為了引入通配符?
Java 中Integer類繼承于Number類,但Integer.class并不是Number.class的子類 (objc比這優(yōu)雅多了),所以Class<Number> numClass = Integer.class;是不合法的。
extends 上界通配符
因為以上尷尬的情況,所以引入了 ? extends 通配符聲明 這個class 引用是用來引用繼承于 Number 的子類 class 對象。所以才能將 Integer.class 賦值給它。
我們以下圖為例子:

image.png
<? extends Fruit> 該類的set()方法失效。但取東西get()方法還有效。
Plate < ? extends Fruit > p = new Plate < Apple > (new Apple()); //不能存入任何元素
p.set(new Fruit()); //Error
p.set(new Apple()); //Error
super 下界通配符
聲明了? super Frult的泛型對象,能存放當(dāng)前類型和它所有父類的對象。

image.png
與 extends 相反,super 通配符會使get()方法失效,但是set()方法仍讓有效。
cast 方法
使用 cast() 可以對無法強制轉(zhuǎn)換的實例進行轉(zhuǎn)換。
public class Demo {
public static void main(String[] args) {
try{
Number a = 1;
Class<Integer> numClass = Integer.class;
Integer i = numClass.cast(a);
System.out.println(i);
} catch(Exception e){
e.printStackTrace();
}
}
}
泛型擦除
- 我們在使用泛型的過程中,常常因為無法獲取泛型 T 的 class 引用。
是因為編譯期在擦除后無法得知對象 T 的類型,但編譯器還是能保證最為返回值時仍然具有 T 類型。因為擦除在運行的邊界(進入和離開的地方),編譯器執(zhí)行了類型檢查并插入了轉(zhuǎn)型代碼。
public class Demo<T> {
private T obj;
public void set(T obj){
this.obj = obj;
}
public T get(){
return this.obj;
}
public static void main(String[] args) {
Demo<String> demo = new Demo<String>();
demo.set("123");
String str = demo.get(); // 等效于 (String) demo.get(),獲取到 Object 類型后強轉(zhuǎn)為 String 類型
System.out.println(str);
}
}
Code:
0: new #3 // class Demo
3: dup
4: invokespecial #4 // Method "<init>":()V
7: astore_1
8: aload_1
9: ldc #5 // String 123
11: invokevirtual #6 // Method set:(Ljava/lang/Object;)V
14: aload_1
15: o #7 // Method get:()Ljava/lang/Object;
18: checkcast #8 // class java/lang/String
21: astore_2
22: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
25: aload_2
26: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
29: return