2010-12-10 71 views
2

好吧,我已經通過googleing查找了文檔,但是我還沒有找到任何真正描述了我想要回答的內容,所以在這裏我問你們。瞭解Java中的繼承和抽象類

所以我得到繼承,以及它是如何工作的。我遇到問題時有時會看到最初定義爲一種類型的對象,並設置爲不同類型,並且我不明白到底發生了什麼。這裏有一個例子:

說我有一個類的動物,並擴展動物類貓和狗。貓,動物和狗都有一種方法說話()爲貓打印「喵」和狗打印「woof」和爲動物「不會說話」。

好吧,最後這裏是我的問題。如果一個貓(c)然後運行Animal a = c ;?如果我運行a.speak();會發生什麼情況;?哪種說法叫做?當我改變那種類型時究竟發生了什麼?我會有任何真正的理由來使用它嗎?

就抽象方法而言,我的問題是具有它們的意義究竟是什麼?在我看到的例子中,他們已經被放入了超類,而它們下面的類定義了確切的行爲。把一個抽象方法放在一個超類中是需要它下面的所有類來實現它的嗎?

感謝您的幫助!

回答

3

究竟發生如果一隻貓 (c),然後運行動物a = c;?如果我運行a.speak(),會發生什麼 ?哪個 說法叫做? 究竟發生了什麼,當我改變 這樣的類型時呢?我會有任何真正的原因 使用這個?

永遠是真實的類的方法,例如在這種情況下,貓的speak()方法。

至於抽象方法去,我 問題是,究竟是什麼讓他們的點 ?

他們確保,例如,每個動物都有一個方法walk(),你可以在每個動物身上呼叫。這是一個保證,說「每個Animal對象有這種方法,你不必關心它」。

在我 看到他們已經投入超類 和其下的類定義的 具體行爲的例子。通過把一個抽象的 方法放在一個超級類中是一個 需要它下面的所有類去實現它的 嗎?

要實現它或抽象,也是的。

2

Cat c = new Cat(); 動物a = c; a.speak()將打印喵。

請檢查java polymorphism了。

約抽象類:

當一個抽象類的子類是, 子類通常提供 實現對所有的在它的父類的 抽象方法。 但是,如果不是,則子類 也必須聲明爲抽象。

JLS的第5.2節解釋了爲什麼Cat可分配給Animal。 (請注意,Animal不是隱含地分配給Cat因爲Cat是一個「更具體的」種類; CatAnimalAnimal亞型是Cat超類型)編譯時引用類型的值的

分配S(源)到編譯時引用類型T(目標)的一個變量,檢查如下:

  • 如果S是一個類類型:
    • 如果T是一個類類型,則S必須與T相同,或S必須是T的子類,否則會發生編譯時錯誤。
    • 如果T是一個接口類型,那麼S必須實現接口T,否則會發生編譯時錯誤。
    • 如果T是一個數組類型,則會發生編譯時錯誤。
  • 如果S是一個接口類型:
    • 如果T是一個類類型,則T必須是Object,或編譯時會出現誤差。
    • 如果T是一個接口類型,那麼T必須是S的接口或S的超接口,否則會發生編譯時錯誤。
    • 如果T是一個數組類型,則會發生編譯時錯誤。
  • 如果S是一個數組類型SC [],也就是說,類型SC的分量的數組:

[不再贅述]

+0

子類型多態性解釋'a.speak()',但它不能解釋爲什麼Cat可以隱式地賦值給Animal。一些更多的細節(JLS鏈接會很好),這將是一個很好的答案。 – 2010-12-10 23:50:07

+0

第5.2節包含*可分配*要求。 http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#184206。如下檢查「編譯時參考類型S(源)的值到編譯時參考類型T(目標)的變量的賦值如下」。 – 2010-12-10 23:57:03

+2

由於Cat延伸Animal,因此'Cat'可以隱式分配給'Animal'。一般來說,如果'A'擴展了'B',那麼'A'可以被賦予'B'。如果'A'實現了一個接口'C',那麼它可以被分配給'C'。這些關係是可傳遞的,所以如果'X'擴展'Y'繼而擴展'Z',那麼'X'的一個實例可以被分配給一個被聲明爲'X','Y'或'Z'中任一個的變量。 。 – 2010-12-11 00:03:37

0

根據你的背景,C++或Java的東西可能會變得非常混亂。

在C++中,虛擬函數的概念在運行時被查找來決定函數屬於哪個實際類。還有非虛函數將根據變量類型調用。

在Java中,雖然所有的方法本質上是虛擬的,這意味着Java方法在運行時總是擡頭一看,這個過程被稱爲運行時多態性

這樣做的好處是這樣

class Animal{ 
    public void type(){ 
     System.out.println("animal"); 

    } 
} 

class Dog extends Animal{ 
    public void type(){ 
     System.out.println("dog"); 
    } 
} 

class Cat extends Animal{ 
    public void type(){ 
     System.out.println("cat"); 
    } 
} 

public class Driver{ 
    public static void main(String[] args){ 
     Animal[] animals = new Animal[3]; 
     animals[0] = new Animal(); 
     animals[1] = new Dog(); 
     animals[2] = new Cat(); 

     for(Animal animal: animals){ 
      animal.type(); 
     } 
    } 
} 

這將輸出

animal 
dog 
cat 
0

據我瞭解,您使用接口來解耦代碼。您想要針對接口進行編程,而不是實現:What does it mean to "program to an interface"?。你將使用一個抽象類來實現對所有實現類來說都是微不足道的函數,所以你不需要把它寫在所有的實現類中。

1

說我有一個類動物,並且教授擴展動物的類貓和狗。貓,動物和狗都有一種方法說話()爲貓打印「喵」和狗打印「woof」和爲動物「不會說話」。

好吧,最後這裏是我的問題。如果製作一隻貓(c)然後運行Animal a = c;究竟發生了什麼?如果我運行a.speak();會發生什麼?哪個speak()方法被調用?當我改變那種類型時究竟發生了什麼?我會有任何真正的理由來使用它嗎?

Java中的對象完全知道它們被創建爲何種類型;它實際上是一個隱藏字段(可以使用Object.getClass()方法檢索)。而且,所有非靜態方法的解析都是從最具體的類的方法定義開始的,然後進入最通用的類​​(Object);因爲在Java中只有一次實現的繼承,所以這是一個簡單的搜索。 Cat知道它是Animal的一個子類型,它是Object的子類型,並且c知道它是一個Cat,與變量的類型無關。

當你做任務,如果該值的已知類型被分配編譯檢查是被分配到或其子的一個類型。如果是,分配工作。如果不是,你需要一個明確的轉換(在運行時放入適當的類型檢查;轉換不能破壞Java的類型系統,它們可能會使其變得更難看)。它並沒有改變方法查找仍然動態完成的事實,並且對象仍然知道它確實是什麼類型;所有的程序正在做的是忽略一些信息。 如果您瞭解C++,那麼請將Java視爲僅具有虛擬方法(以及靜態方法),並且將查找處理到vtable中非常簡單,因爲繼承鑽石或其他惡意案例沒有問題。

當處理接口的實現時,除了執行更復雜的查找(即先前查找vtable中的索引,然後再繼續處理之前),它幾乎是相同的。然而,要有一個實現接口的對象意味着必須有一個完全實現接口的類,那麼這個接口再次相對簡單。請記住,所有非常複雜的東西都是在編譯時完成的;在運行時,事情在所有情況下都是相對直接的。

那麼,你會利用這一切嗎?那麼,你應該應該(你真的很難避免真正的代碼)。從界面或超類定義合同的角度來看,這是一種很好的風格,在這種情況下,子類遵守合同,而不需要調用者知道細節。 Java庫非常重要,特別是因爲合同類型細節的可見性及其履行情況可能不同。所有的客戶端代碼都知道對象服從合同,它是給定的類型。

+0

你提出的其他問題幾乎被其他人回答,並且在許多其他面向對象的語言中有幾乎相同的答案。 – 2010-12-11 01:22:15