# 多型

> 介紹多型的觀念及程式基本撰寫方式。

## 何謂多型？ What is Polymorphism?

多型 Polymorphism 一詞是指生物學中一個生物或物種可以有不同的形式或階段。 在物件導向程式設計的概念中，利用父類別提供的方法呼叫，子類別可以有自己特有的行為。

舉個例子：

```java
class Animal {
    void move() {
        System.out.println("move...move...");
    }
}
class Dog extends Animal {
    void move() {
        System.out.println("跑...跑...");
    }
}
class Bird extends Animal {
    void move() {
        System.out.println("飛...飛...");
    }
}
class Fish extends Animal {
    void move() {
        System.out.println("游...游...");
    }
}
```

我們有Animal類別，以及Dog、Bird、Fish各自繼承Animal。 ![](https://1201963393-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MLRnhT9aTkN9HvaBP28%2Fsync%2F46ffa3d3f318ad450eac619ff62d5c8285f87a8f.jpg?generation=1604653626083846\&alt=media)

還記得繼承關係可以用『is a』來表述嗎？ 我可以說 Dog is a Animal. 或 Bird is a Animal. 或 Fish is a Animal. 這是OK且符合邏輯的。

好，那現在我有4隻動物，是什麼動物不管，我就把牠們都當成動物就好。 我知道動物裡面有一個move()的方法，我讓每隻動物都使用這個move()的方法。

程式如下：

```java
class Test {
    public static void main(String[] args) {

        Animal a =new Animal();
        Animal b =new Dog();
        Animal c =new Bird();
        Animal d =new Fish();

        a.move();
        b.move();
        c.move();
        d.move();

    }// end of main(String[])
}// end of class Test
```

變數a看起來OK，宣告是Animal且也用Animal的建構子去初始化。

變數b看起來就怪怪的，用Dog()來初始化的話不是應該宣告成Dog型態嗎？ 是的，那樣寫也OK，我們知道依據繼承關係，『Dog is a Animal.』所以我這樣寫也並非違反邏輯。 利用Dog()建構子創造的物件，理當擁有Dog類別定義的所有成員，但不管，現在我們只把這個物件當作Animal來看。

變數c，不管你怎麼建構你的物件，我都會用Animal的角度去看你。

變數d：本體是Fish，但有人把我當成動物。 (變數b,c,d是差不多情況)

好，現在我不管牠們4個的『本質』是什麼，我只知道我把牠們都當做 Animal 來看，而我知道 Animal 裡面有定義一個方法叫做 move()，所以把4隻Animal的move()都呼叫看看。

執行結果：

```java
move...move...
跑...跑...
飛...飛...
游...游...
```

可以看到，雖然都宣告為 Animal，也都呼叫了Animal裡面的move()方法，但每個物件表現出來的都不一樣，因為他們的『本質』不一樣。

這就是多型 Polymorphism 的簡單範例。

從上面的例子可以看出，我們只要管他是不是動物就好，不用管他的本質是什麼 ，就可以對他進行處理。

```java
 void moveAnimal(Animal ani){
     ani.move();
 }
```

只要可以視為Animal的物件，就可以使用這個方法而不會出錯。 這樣的設計方式可以**降低方法定義對類別的依賴**，使用一個制定好的介面，利用該介面來操作不同的物件，增加程式的彈性及可維護性，設計上也比較有架構。

如果沒有多型的話，

要設計一個方法用來呼叫 Animal、Dog、Bird、Fish的移動move()方法，會變成這樣：

```java
void  moveAnimal(Animal ani){
    ani.move();
}
void  moveDog(Dog dog){
    dog.move();
}
void  moveBird(Bird bird){
    bird.move();
}
void  moveFish(Fish fish){
    fish.move();
}
```

在有多型的情況下，我知道Dog、Bird、Fish都可以被當成Animal，而我們要呼叫的move()也剛好有被定義在Animal中，所以只需要制定一個可以操作Animal這個介面的方法就好。

```java
void moveAnimal(Animal ani){
  ani.move();
}
```

使用這個方法，就可以對所有『可以被當成Animal的物件』進行操作：

```java
class Test {
    public static void main(String[] args) {

        Animal a=new Animal();
        Dog d =new Dog();
        Bird b = new Bird();
        Fish f =new Fish();

        moveAnimal(a);
        moveAnimal(d);
        moveAnimal(b);
        moveAnimal(f);

    }    
    static void  moveAnimal(Animal ani){
        ani.move();
    }
}// end of class Test
```

執行結果：

```java
move...move...
跑...跑...
飛...飛...
游...游...
```

## 隱性轉型 Implicit Casting

在上面程式中，我們定義了moveAnimal(Animal ani)的方法，他會接受一個Animal參數進來，但程式中我們卻給他Dog、Bird、Fish這些類別的物件，為什麼能動？

因為Java會幫你進行型態轉換，子類別必定擁有母類別的所有屬性、所有方法，所以子類別一定可以轉型為母類別，而不會出錯。

```java
Dog d = new Dog();
Animal a1 = d;  // Java幫你作了型態轉換，但你看不到，等價於下行
Animal a2 = (Animal)d;  // 自己寫是一樣的

moveAnimal(d); // 等價於下行
moveAnimal((Animal)d);
```

因為這個機制，所以搭配多型的設計方法，程式撰寫上變得非常便利。

## 轉型失敗 Casting Fail

我們知道利用小括號裡面放型態可以進行強制型態轉換，但會不會有問題？

以繼承來說，子類別是母類別的『延伸』，所以子類別轉型成母類別是絕對OK的，因為擁有母類別的所有欄位、所有方法。 但若母類別轉型成子類別呢？

範例程式：(Dog 繼承 Animal)

```java
Animal ani = new Animal();
Dog d = (Dog)ani;  // 母類別強制轉型為子類別
```

執行結果：

```java
Exception in thread "main" java.lang.ClassCastException:
Animal cannot be cast to Dog
```

沒錯，執行的時候發生了錯誤，產生ClassCastExeption例外。

強制轉型要付出的代價是工程師自己要負責的，以上述程式來說，『編譯會過，執行會錯』，因為母類別不一定擁有子類別的欄位，所以不能這樣轉換。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yubin551.gitbook.io/java-note/object_oriented_programming/polymorphism.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
