# 抽象

> 介紹關鍵字 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. 方法

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

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


---

# 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/abstract.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.
