1. 程式人生 > >Think in java(五)RTTI的的三種形式、型別資訊、class.forname與.class的區別

Think in java(五)RTTI的的三種形式、型別資訊、class.forname與.class的區別

一、什麼是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的新特性基本上沒有任何用處。