2013-10-05 145 views
4

好。所以, 當你擁有類如對象類型聲明

public class A {...} 

和層次結構,

public class B extends A {...} 

...當你創建對象,是有什麼區別:

A object = new A(); 
A object = new B(); 
B object = new B(); 

謝謝你的時間。

+0

當您創建('new')一個A時,您創建了一個A.當您創建一個B時,您創建了一個B.但B包含了A的所有功能,並且在大多數情況下可以像A一樣使用。如果在一個帶有「A」類型的變量中引用B,則不能訪問該對象的「B-ness」,而只能訪問「A-ness」。但是,您可以將參考「轉換」回「B」以重新獲得它的「B-ness」:'B refB =(B)refA;'但演員('(B)')在運行時會失敗如果refA實際上沒有引用B. –

回答

1
A object = new B(); 

此聲明object將指A類的對象或其任何亞類的(當它不是null)。編譯器會將它視爲A類型的對象,因此您只能訪問爲A(或其超類之一)聲明的方法和字段。這也意味着,你可以在以後將其分配給任何其他對象A類或子類:

A object1 = new B(); 
B object2 = new B(); 

// reassign later 
object1 = new A(); // legal 
object2 = new A(); // ILLEGAL 

class C extends A { ... } 
object1 = new C(); // legal 
object2 = new C(); // ILLEGAL 

因此,最初的聲明聲明object具有類型A。但其初始值是B類型的對象,這是可以的,因爲BA的子類。

這應該解釋你的第二個和第三個例子的區別。第一個和第二個之間的區別僅僅是(在運行時)第一個創建了一個類型爲A的新對象,第二個創建了一個類型爲B的新對象。

2
public class A 
{ 
    public void methodA(){} 
} 
public class B extends A 
{ 
    public void methodB(){} 
} 

我希望這可以證明不同之處。

A obj = new A(); 

a.methodA(); //works 

A obj = new B(); 

obj.methodA(); //works 
obj.methodB(); //doesn't work 
((B)obj).methodB(); //works 

B obj = new B(); 

obj.methodA(); //works 
obj.methodB(); //works 
1
A object = new A(); 

A類型的object(你可以訪問從A字段或方法)

A object = new B(); 

A類型的object(你不能從B訪問場或方法中,只有從A

B object = new B(); 

objectB類型(可以從AB訪問場或方法)

1
A object1 = new A(); 
A object2 = new B(); 
B object3 = new B(); 

object1的被聲明爲到A對象的引用。由於B類延伸了A類,因此可以將其設置爲或((new A()new B()將是有效的)。

object2被聲明爲對A對象的引用,但實際上是B對象。假設B類有一個名爲eatFood()的方法。如果您嘗試使用object2.eatFood()來訪問該方法,編譯器會拋出一個錯誤,因爲eatFood方法僅在B類中。即使對象實際上是一個B對象,由於類型聲明,編譯器認爲它是一個A對象。要訪問eatFood方法,您必須註明它:((B)object2).eatFood()

object3只是對B對象的引用,實際上是B對象。它可以訪問A方法以及B方法。

+1

我相信應該是'((B)object2).eatFood()',因爲演員操作符的優先級...我自己一直在遇到這個問題。 – ajb

2
A object = new A(); 

您在類型A的引用中創建A instance。您可以僅訪問方法/屬性和父項方法/屬性。

A object = new B(); 

你正在創造型A的參考B instance這樣object可能表現在多態的方式,例如,如果你讓object.method()method在B重寫,然後它會調用這個重寫方法。你必須小心不要打破Liskov Substitution Principle。您只能訪問A方法/屬性和父項方法/屬性。當您只需要超類型合同時,這是首選方式。

B object = new B(); 

你正在創建在B類型的參考變量B instance。您只能訪問B方法/屬性和父項方法/屬性。

1

A var = new B(); 

的線是一種用於兩個單獨的步驟的速記的。

A var;   // (1) Make a variable of TYPE A. 
var = new B(); // (2) Make an object of CLASS B, that from now on may be 
       // referred to by the variable var. 

所以一個變量有一個TYPE,一個對象有一個CLASS。他們經常匹配。變量的類型通常實際上是一個類,儘管不一定。理解變量類型和變量引用的對象的類別之間的區別很重要。

一個對象通常屬於多個類。如果B類擴展A類,這意味着B類的所有對象也是A類的對象。任何類的所有對象也都是類Object的對象。換句話說,當我們說對象是一個B時,這比說它是一個A更具體。就像當我們說瑜珈是一個時,這比說瑜珈是動物更具體,因爲所有動物

因此,A類型的變量確實可以引用類B的對象,如果A是B延伸的類。但是如果你有一個A類型的變量,你不能用它來做特定於B類對象的事情。例如,假設A類有一個叫display()的方法,而B類有一個叫做explain()的方法。編譯器會讓你在A類型的變量上調用display(),但它不會讓你調用explain()。如果是這樣,那麼試圖撥打explain()這個實際上不是B的對象會冒失敗的風險。

因此,無論何時有類B定義的方法,您都需要一個B類型的變量才能調用它們。當然,你也可以使用同一個變量來調用在A類中定義的方法。從某種意義上說,如果B類擴展了A類,那麼B類型的變量比A類型的變量更強大 - 你可以用它做更多的事情。

於是問題出現了 - 爲什麼我曾經想要寫

A var = new B(); 

當B類型的變量會比在這個例子中var更厲害?

簡單的答案是它可以與查看代碼的人交流。它說,「是的,我知道這個變量是指B,但實際上我只打算使用A類提供的方法。這對於試圖瞭解您的代碼或維護它的人來說實際上會有幫助。」

還有一些情況下,它可以使涉及該變量的方法調用真正的改變。假設還有另外一個C類,它有兩個方法名稱相同但略有不同的簽名,是這樣的。

public class C { 
    public void process(A arg){ 
     // Do some stuff 
    } 

    public void process(B arg){ 
     // Do some other stuff 
    } 
} 

在這特殊情況下,被調用的版本process取決於變量的類型,而不是對象的類別。因此,如果您編寫

C processor = new C(); 
A var = new B(); 
processor.process(var); 

這將調用process的第一個版本 - 簽名中帶A的那個版本。由於變量的類型。但是,如果你寫

C processor = new C(); 
B var = new B(); 
processor.process(var); 

這將調用process第二個版本 - 一個爲B的簽名。