Think in java(五)RTTI的的三種形式、型別資訊、class.forname與.class的區別
阿新 • • 發佈:2019-02-02
一、什麼是RTTI,為什麼需要RTTI
RTTI全稱為Run-Time Type Identification,執行階段型別識別,含義就是在執行時,識別一個物件的型別。他使得從只能從編譯期執行面向物件型別的操作的禁錮中解脫出來,並且可以使用某些非常強大的程式。RTTI有三種形式。
- 傳統的型別轉換,由RTTI確保型別轉換的正確性,如果執行了錯誤的型別轉換,會丟擲一個ClassCastException的異常。
- 代表物件型別的Class物件,通過查詢Class物件獲取所需要的資訊
- 型別轉換前用instanceof檢查
二、傳統型別轉換
package classtest; import java.util.Arrays; import java.util.List; abstract class Shape { void drow(){ System.out.println(this+" draw()");} abstract public String toString(); } class Circle extends Shape{ public String toString(){ return "circle"; } } class Square extends Shape{ public String toString(){ return "Square"; } } class Triangle extends Shape{ public String toString(){ return "Triangle"; } } public class Shapes{ public static void main(String[] args){ List<Shape> list = Arrays.asList( new Circle(), new Square(), new Triangle()); for(Shape s:list){ s.drow(); } } }
//輸出結果:
circle draw()
Square draw()
Triangle draw()
基類中通過this將型別引數傳遞給print,然後呼叫對應的toString方法,這也是多型的含義。
三、通過Class物件來執行RTTI
1.class物件
Class物件包含了與型別有關的資訊,Java使用Class物件來執行RTTI。每個類都有一個Class物件,所有的Class物件都屬於Class這個類。這個類有一些常用的方法如下:
- Class.forName("classname"),獲取classname類對應的Class物件的引用,try/catch環繞
- getName()
- getSimpleName()
- getCanonicalName()
- getInterfaces()
- getSuperclass()
- newInstance(),try/catch環繞,通過對應的Class物件的引用獲取一個classname類的例項
- getclass(),通過classname類的例項獲取Class物件的引用
2.類字面常量.class以及與class.forname的區別
獲取一個Class物件的引用,出了上面提到兩種方法外,還可以通過classname.class來獲取。這種獲取方法稱為類字面常量。這樣做不僅簡單,而且更加安全,因為他在編譯期就得到了檢查。所以也不用try/catch來環繞。
另外,還有一點區別就是使用.class來建立Class物件的引用時,不會自動地初始化該Class物件。
3.泛型與Class物件
傳統建立一個引用的方法是
- Class intClass = int.class
這樣沒有泛型的限制,後面intClass=double.class也會通過編譯。所以應該使用下面的語法
- Class<Integer> intClass = int.class
- Class<? extends Number> intClass = int.class
- Class<?> intClass = int.class //該方法與傳統方法的區別是,你告訴別人你是故意這麼做的。
4.泛型類的語法
package classtest;
import java.util.*;
class Counter{
private static int count;
private final int id = count++;
public String toString(){
return Integer.toString(id);
}
}
public class FilledList<T> {
private Class<T> type;
public FilledList(Class<T> type){this.type=type;}//這裡不能寫成public FilledList<T>
public List<T> create(int n){
List<T> list = new ArrayList<T>();
try{
for(int i=0;i<n;i++){
list.add(type.newInstance());
}
}catch(Exception e){throw new RuntimeException(e);}
return list;
}
public static void main(String[] args){
FilledList<Counter> f
=new FilledList<Counter>(Counter.class);
System.out.println(f.create(15));
}
}
該語法實現了儲存了一個類引用,然後又產生了一個List,填充這個List的物件是使用的newInstance()的方法。
//輸出結果:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
四、instanceof與新轉型語法
RTTI的第三種形式是用關鍵字instanceof,在強制向下轉型前使用instanceof是很有用的
if(x instanceof Dog)
(Dog)x.bark();
此外。Class類的cast()方法和asSubclass()兩個SE5的新特性基本上沒有任何用處。