搜尋

首頁  >  問答  >  主體

java继承中,为什么向上转型后无法运行子类中特有的方法?

看了这篇文章:
https://www.ibm.com/developerworks/cn/java/j-lo-polymorph/
有一些疑问。

class Person { 
 public String toString(){
    return "I'm a person."; 
 } 
 public void eat(){} 
 public void speak(){} 
    
 } 

 class Boy extends Person{ 
 public String toString(){ 
    return "I'm a boy"; 
     } 
 public void speak(){} 
 public void fight(){} 
 } 

 class Girl extends Person{ 
 public String toString(){ 
    return "I'm a girl"; 
     } 
 public void speak(){} 
 public void sing(){} 
 }
//最后这么用的话,
Person girl = new Girl(); 
girl.speak(); 

按照作者的说法,girl引用变量指向堆中的 Girl对象,所以运行的时候知道当前对象是Girl对象,找Girl的方法表,
找到后执行。这对于speak方法来说是行得通的。
但是如果这样呢:
girl.sing();
按照作者的说法,去Girl方法表找到sing方法,然后执行。
实际情况大家都知道有错误,编译不通过。
这中间肯定漏了点我不知道的什么?希望可以赐教。
换个方式问也就是:
当发生向上转型的时候,栈和堆中分别发生了什么?

怪我咯怪我咯2767 天前479

全部回覆(4)我來回復

  • PHPz

    PHPz2017-04-17 17:21:39

    原始碼就是這個樣子,還是有省略的東西,把完整程式碼貼一下吧,按你程式碼現在這個樣子,你是沒把
    Person girl = new Girl()
    girl.speak()
    這兩句應該放在main裡面,你直接在類別中調用,結構不對呀

    回覆
    0
  • 黄舟

    黄舟2017-04-17 17:21:39

    是變數的宣告型別決定了它能呼叫哪些方法,而不是它所指向的堆中的物件的實際型別。

    像你這樣:

    Person girl = new Girl();
    girl.speak(); 

    girl的宣告類型是Person,而Person並沒有speak這個方法,所以會報錯。注意是編譯器報的編譯期的錯誤,編譯器是不管你那個girl執行的型別到底是什麼。 girl的声明类型是Person,而Person并没有speak这个方法,所以会报错。注意是编译器报的编译期的错误,编译器是不管你那个girl执行的类型到底是什么。

    运行期,虚拟机当然会知道girl执行的堆中对象是Girl类型。

    编译器为什么要根据变量的声明类型来决定能不能调用某个方法?其中一个原因就是,编译期间可能根本不知道这个变量在运行期绑定的到底是什么类型的对象。举个例子:

    Person p = Math. random() > 0.5 ? new Girl() : Boy();

    这个例子中的p

    運行期,虛擬機器當然會知道girl執行的堆中物件是Girl類型。 🎜 🎜編譯器為什麼要根據變數的宣告類型來決定能不能呼叫某個方法?其中一個原因是,編譯期間可能根本不知道這個變數在運行期綁定的到底是什麼類型的物件。舉個例子:🎜 rrreee 🎜這個範例中的p不到運作時根本無法確定它所綁定的物件的真實類型。 🎜

    回覆
    0
  • ringa_lee

    ringa_lee2017-04-17 17:21:39

    題主的問題涉及到了多態性概念。下面用題主的例子來說明:

    假設使用主題定義的三個類別PersonGirlBoy。當PersonGirlBoy。当

    Person ps = new Girl(); 

    定义一个对象ps以后,它在编译时的类型是Person,而实际运行时的类型是Girl

    在使用ps这个对象时,程序只知道它是个Person,而一个Person只有eatspeak两个方法,所以在调用对象成员的时候只能调用eatspeak两个方法;程序并不知道ps对象的实际类型是Girl,所以不能调用Girl独有的sing方法。即:

    ps.eat();    // OK,调用父类实现
    ps.speak();  // OK,调用子类实现
    ps.sing();   // Error

    那么,既然程序并不知道ps对象的实际动态类型是Girl,为什么ps.speak();还能调用Girl rrreee 定義一個物件ps以後,它在編譯時的型別是Person,而實際執行時間的型別是Girl

    在使用ps這個物件時,程式只知道它是個Person,而一個Person只有eatspeak兩個方法,所以在呼叫物件成員的時候只能呼叫eatspeak兩個方法;程式不知道< code>ps物件的實際型別是Girl,所以不能呼叫Girl獨有的sing方法。即:

    rrreee

    那麼,既然程式並不知道ps物件的實際動態類型是Girl,為什麼ps.speak();還能呼叫Girl中的實作呢?這就是

    多態

    的表現。 #🎜🎜# #🎜🎜#對於一個指向子類別的父類別指標/引用,如果子類別中重寫了父類別的方法,那麼在透過父類別指標/引用呼叫這個方法的時候,編譯器會在編譯時把這個父類別的方法呼叫動態綁定到實際類型的方法上去。而那些沒有被子類別重寫的方法,則會靜態綁定到父類別的方法上去。 #🎜🎜# #🎜🎜#這是從抽象角度來理解,更底層的實現方法就要參考具體實現了,在題主提供的文章中講的比較清楚。 #🎜🎜#

    回覆
    0
  • 大家讲道理

    大家讲道理2017-04-17 17:21:39

    Stack中是person ,heap中是girl,person類別沒有sing方法的參考。

    回覆
    0
  • 取消回覆