2009-01-31 101 views
3

關於在Java中鑄造參考變量,我有一些不清楚的地方。在Java中鑄造參考變量

我有兩個類A和B,A是超類B. 的如果我有兩個對象,然後將打印聲明:

A a = new A(); //superclass 

B b = new B(); //subclass 

System.out.println ((A)b); 

那麼究竟是什麼發生時println方法執行?

我知道,由於B是A的子類,允許我進行以下轉換:

A a2 = (A)b; 

我還知道,當的println取參考變量作爲參數,則toString()方法(已經創建了對象參數)被調用(隱式地)。這是因爲println()方法正在尋找一個String類型的參數,並且toString()方法將該對象表示爲一個字符串。即使我們不寫toString(),也會隱式地調用該方法。所以,下面的兩個語句是等價的:

System.out.println (b); 

System.out.println (b.toString()); 

所以,我的問題是:是採取什麼樣的隱含動作時,我們有

System.out.println ((A)b); 

我假設參考變量b的類型自動從B到A的可變仍然應該指向相同的對象改變 - 一個與

B b = new B(); 

創建,但B的僅僅是類型現在會改變。它是否正確? 另一個問題:即使我已將b的類型更改爲超類的類型,子類中的overriden方法將被調用,而不是超類的方法?

非常感謝。

問候

回答

1

這是正確的嗎?

的排序。轉換表達式的結果將是A類型的。該類型的「B」變量會一直保持型B.

另一個問題:即使我已經改變了類型b與超類的類型,是在子類中重寫的方法將是稱爲,而不是超類的那些?

將會調用基礎對象的實例方法。例如:

class Foo { 
    public static void main(String[] args) { 
     B b = new B(); 
     assert "B".equals(((A) b).m()); 
    } 
} 

class A { 
    String m() { return "A"; } 
} 

class B extends A { 
    String m() { return "B"; } 
} 
1

由於Java方法都具有動態分派功能,因此被調用的函數不依賴於引用的靜態類型。因此,無論演員是否演員,結果都是一樣的。 [結果可能會有所不同,如果你是向下傾斜的 - 鑄造版本可能會拋出異常]

4

演員在這種情況下沒有影響。System.out.println(XXX)獲取不同類型的參數(多個重載版本),但在這種情況下,您將獲得使用Object的版本。由於Java中的每個對象都支持toString(),因此toString會在實際參數上調用,而不管它是什麼。

現在,由於Java中的所有方法都是動態分派的,因此運行的版本是與動態類型相對應的版本。將B的對象轉換爲A只會更改表達式的靜態(聲明)類型。動態類型(真正在那裏)仍然是B.因此,B中的版本被調用。

+0

如果傳遞的參數是String,則不會調用toString()。在其他情況下是的。 – 2009-01-31 04:37:25

1

始終將您的對象視爲它實例化的類型(在您的案例中爲B)。如果它被認爲是 - 嗯 - 把它看成是穿上A衣服。它可能看起來像一個A,你可能無法做任何你想做的漂亮B的東西,但在衣服裏它仍然是B - 衣服根本不會改變底層的物體。

所以總結是 - 你只能調用一個方法,但是當你調用它,它直接通過並執行它,因爲它會如果它是一個B.

3

有許多聲明println(...)PrintStream類(這是System.out的類型)。

其中兩個是:

void println(String x) 
void println(Object x) 

當你調用println((A)b)編譯器選擇調用println(Object)因爲A不是String(或任何其他類型的println支持的)。當你調用println(b.toString())時,編譯器選擇println(String),因爲你的傳遞一個字符串。

就你而言,鑄造bA不起作用,因爲println()沒有A或B類型的聲明。但是演員陣容仍然會出現(因爲你需要這樣做),或者也許不會,因爲編譯器會優化它,因爲它知道它是多餘的,它不會失敗並且沒有任何作用。

它不是慣用寫:

A a2 = (A)b; 

,因爲這是多餘的,因爲B是可能的情況是,編譯器將優化掉鑄造(這是一個運行時間操作,以A的一個子類檢查對象是否屬於特定類型,絕對不要更改它的類型)。

一旦構造了B類型的對象,它的類型爲從來沒有的變化。它始終是一個B:

class B extends/implements A {...} 
B b = new B(); // construct a B 
A a = b;   // assign a B to an A variable, it's superclass 
A a = (A) b  // as above including check to see that b is an A (redundant, may be optimised away). 

B b = a;   // Syntax error, won't compile 
B b = (B) a  // Will check whether a is of type B then assign to variable b 

在最後一種情況,因爲BA一個子類,它可能是一個持有B實例和轉換才能成功。或者它可能是一個持有其他類的實例,它擴展/實現/是A,而不是B,您將得到ClassCastException。因此,由於類型B的對象總是保留它的身份(它是「B」性),因此對該對象調用的任何(實例)方法將始終調用B的實現,而不管通過其訪問對象的變量被宣佈爲A或B.

請記住,您只能調用在類中聲明的變量被定義爲的方法。例如,如果B聲明瞭一個方法b_only(),那麼編譯器將不允許你編寫a.b_only();如果B聲明方法b_only(),那麼編譯器將不允許你編寫a.b_only();如果B聲明方法b_only(),那麼編譯器將不允許你寫a.b_only();但你可以寫((B)a).b_only()

0

如上所述,由於被覆蓋的方法被動態綁定,所以在這種情況下投射是無關緊要的。由於toString存在於所有對象中,所以它符合此條件,因此要在運行時確定要調用的對象類型和方法。

請注意,雖然所有方法都不是這樣,因爲只有重寫的方法是動態綁定的。重載的方法是靜態綁定的。這裏的許多答案都提到java方法總是動態綁定的,這是不正確的。

See this question for a more detailed explanation.

1

我認爲,當我們在java和通過使用該變量我們可以分配任何類型的對象使用參考變量。大多數情況下,我們創建一個Interface和抽象類的引用變量,因爲我們無法創建接口和抽象類的對象,因此在Interface或Abstract類的引用變量中分配類的對象。 EX-

Interface X { 
public abstract void xx(); 
public abstract void yy(); 
} 

Class XXX implements X { 
    ........... 
} 

Class XY extends XXX { 
    X xy = new XXX(); 
} 

這裏的xy是界面X的基準,併爲其分配類別XXX的對象接口的基準。

所以根據我的觀點通過使用引用變量我們也可以使用接口來參與對象的創建。

0

問題:即使我已將b的類型更改爲超類的類型,子類中的overriden方法將被調用,而不是超類的那些方法?

在這種情況下,調用小類b的方法;令人信服地理解爲什麼;你可能會涉及到以下現實世界中的場景

考慮父類父親表現出的行爲(方法):高度 定義爲

父親是高大,身高= 6'2"

兒子是一個子類繼承自父高度行爲;結果他也是高;高度爲6' 顯然壓倒一切的行爲

每當您的子類Son調用其名稱的行爲高度時,他會顯示覆蓋的行爲,即他自己的身高6'