# 抽象

> 介紹關鍵字 abstract 的用法及其物件導向意義。

## 何謂抽象？ What is Abstract?

抽象可以想像成一種概念，並不存在於世界上。

在『多型』的章節中，我們有一個Animal的類別，但現實生活中有這個東西嗎？ 動物只是一個概念，並沒有一種生物叫作動物。 狗、鳥、魚都是實際存在於世界上的實體，**他們都是動物，現實中卻沒有一個實體叫作動物。**

如果聽起來卡卡的，再反覆讀幾次。 所以我們可以把動物視為一種『概念』，他是抽象的，不存在於世界上的。 而Dog、Bird、Fish都具備這個概念的特質，所以繼承Animal。

## 關鍵字 abstract

abstract 是一個Java的關鍵字，是一個修飾子，可以用來修飾的有： 1. 類別 2. 方法

## 抽象類別 abstract class

利用abstract來修飾類別，可以使類別變成抽象類別，使用方法：

```java
abstract class 類別名稱{
    // 成員定義...
}
```

在類別定義前加上修飾子abstract，就可以定義抽象類別。

上面我們提到，抽象是一個概念，而不是一個存在的實體。同樣的，**抽象類別不能被實體化**。

範例程式，抽象類別定義：

```java
abstract class Animal{
    int height,weight;
    void move(){
        System.out.println("move...move...");
    }
}
```

範例程式，實體化測試：

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

        Animal ani = new Animal();

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

執行結果：

```java
Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
    Cannot instantiate the type Animal
```

發生編譯錯誤，因為**抽象類別無法被實體化**。

阿我設計類別就是為了實體化產生物件阿，現在跟我說這個類別無法被實體化那我幹嘛要寫？ 先別急，這個抽象類別無法被實體化，但是他可以『被繼承』。

```java
class Dog extends Animal{

}
```

這樣Dog類別就有Animal類別定義好的東西，在加上他自己的成員，就可以用來創造Dog的物件。

因此可以說，**抽象類別是用來被繼承的**。

擁有這個概念的類別，繼承抽象類別，在程式撰寫上就可以利用多型的概念上程式更有邏輯性。

以上一篇『多型』的程式例子來說，Animal這個抽象的概念不應該被實體化，因為現實中不從在一種東西叫作『動物』，而狗、鳥、魚都是實際存在的東西，都擁有『動物』這個概念的特性，所以比較好的寫法應該定義為：

```java
abstract class Animal {
    // 成員定義
}

class Dog extends Animal {
    // 成員定義
}

class Bird extends Animal {
    // 成員定義
}

class Fish extends Animal {
    // 成員定義
}
```

這樣寫很清楚告訴說，動物是一種概念，不能被實體化。而Dog、Bird、Fish具有動物的特性，可以被實體化存在於世界上(記憶體中)。

## 抽象方法 abstract method

利用修飾子abstract，可以使方法變成抽象方法，抽象方法只能寫方法的原型，無法定義方法的本體。

> 方法的原型？ 只定義修飾子、回傳值型態、方法名稱、參數型態，而沒有『大括號{}』的部分就是方法原型。
>
> 方法的本體就是用『大括號{}』定義的東西，有寫{}就算有定義，不管裡面有沒有程式敘述。

使用格式：

```java
abstract <修飾子> 回傳型態 方法名稱(<參數...>);
```

範例：

```java
abstract void eat();
```

錯誤範例：

```java
abstract void eat(){    // 編譯錯誤：Abstract methods do not specify a body
    // 不管有沒有寫東西，都編譯錯誤，抽象方法不能定義方法本體
}
```

好，那現在問題又來了，我要這個沒有本體的抽象方法幹嘛？ 只有原型的方法做不了任何事阿！

沒錯，所以必須要有其他人『覆寫』這個抽象方法。

程式範例，定義Animal為抽象類別，有一個抽象方法eat()：

```java
abstract class Animal {
    abstract void eat();
}
class Dog extends Animal{
    void eat(){
        System.out.println("啃骨頭...");
    }
}
```

在上述程式中，母類別Animal定義了一個抽象方法eat()，宣告這個eat()方法回傳值是void且不帶任何參數。 子類別必須覆寫這個方法，為這個方法定義本體，也就是『實作implement』這個方法。

特別注意的是，**抽象方法只能定義在抽象類別中**。 (否則會編譯錯誤)

abstract method can only be defined by an abstract class

有了抽象方法，那我們的抽象類別就可以定義的更簡潔了。

範例程式：

```java
abstract class Animal{
    int weight,heigh;
    void setValue(int w,int h){
        this.weight = w;
        this.height = h;
    }
    abstract void eat();
}
class Dog extends Animal{
    void eat(){
        System.out.println("啃骨頭...");
    }
}
class Bird extends Animal{
    void eat(){
        System.out.println("早起吃蟲...");
    }
}
```

上述程式中，將Animal類別定義為抽象，裡面把eat()定義為抽象方法，任何繼承這個Animal的子類別都必須實作屬於他們自己的eat()，同時都擁有Animal所定義的成員。

這樣做的目的是因為繼承一個類別，子類別就可以當作母類別來使用，但一般母類別都定義的很一般化，所以透過abstract關鍵字『強制』子類別繼承這個母類別後**必須實作屬於自己的方法**，也讓物件導向的設計邏輯更符合一般的現實概念。

## 最後整理一下

關鍵字abstract，抽象，可以修飾：

1. 類別

   讓類別變成抽象類別，不能建立實體，專門用來『被繼承』。
2. 方法

   讓方法變成抽象方法，只能定義原型，專門用來『被覆寫』。

其中抽象方法只能被定義在抽象類別中。
