---
# System prepended metadata

title: Extends & Constructor
tags: [Java]

---

---
tags: Java
---

# Extends & Constructor

## 懶人包

- 如果父類別有定義 constructor，且**有參數**，那子類別一定要呼叫 `super()` 並帶入對應的引數

所以
- 盡量所有 class 都有 default constructor
- 盡量所有子類別都要在 constructor 呼叫 `super()`

## Constructor

- 和 class 名稱相同的 method
- 用來**初始化**物件的 properties
- Method 可以 [**overloading**](http://hsingjungchen.blogspot.com/2017/04/java-overloadingoverriding.html)，所以同一個 class 可以有多個 constructors，但是不同的 constructors 參數不能相同
    - **Exp**:
        ```java=
        class Data {
            int a, b;

            public Data() {
                a = 0;
                b = 0;
            }

            public Data(int x, int y) {
                a = x;
                b = y;
            }

            public void print() {
                System.out.printf("(%d, %d)\r\n");
            }
        }

        class Main {
            public static void main(String[] args) {
                Data d1 = new Data();      // 用第4行的 Data() 初始化
                Data d2 = new Data(2, 3);  // 用第9行的 Data(int, int) 初始化

                d1.print();
                d2.print();
            }
        }
        ```
        **Output**:
        ```
        (0, 0)
        (2, 3)
        ```

<br>

- 除了直接 assign，也可以在 constructor 裡面運算
    - **Exp**
        ```java
        class Data {
            int a, b;

            public Data(int x, int y) {
                a = 2 * x + y;
                b = 6 * y + 3;
            }

            public void print() {
                System.out.printf("(%d, %d)\r\n", a, b);
            }
        }

        class Main {
            public static void main(String[] args) {
                Data d = new Data(2, 3);
                d.print();
            }
        }
        ```

        **Output**
        ```
        (7, 21)
        ```

## Default Constructor

- 沒有參數的 constructor
    - **Exp**
        ```java
        class Data {
            int a, b;

            public Data() {
                a = 0;
                b = 0;
            }

            public void print() {
                System.out.printf("(%d, %d)\r\n", a, b);
            }
        }

        class Main {
            public static void main(String[] args) {
                Data d = new Data();
                d.print();
            }
        }
        ```
      **Output**:
        ```
        (0, 0)
        ```

### Compiler-Generated Constructor

- 當你的 class **沒有定義任何 constructor 時**，compiler 會自動產生一個 default constructor
- 它只會在編譯的時候加到你的 class 中，原始的程式碼檔案不會改變
    - **證明**: 即使沒有定義 `Data()`，還是可以用 `new Data()` 建立物件，說明這個情況下 `Data()` 確實是存在的
        - **Exp**
            ```java
            class Data {
                int a, b;

                public void print() {
                    System.out.printf("(%d, %d)\r\n", a, b);
                }
            }

            class Main {
                public static void main(String[] args) {
                    Data d = new Data();
                    d.print();
                }
            }
            ```
            **Output**
            ```
            (0, 0)
            ```

<br>

- 會用預設的方式初始化 properties
    - **Primary type** (`int`, `float`, `char`, `boolean`, ...)
        - 會被設為 0 (在記憶體裡面的 bits 全為 0)
        - 對 `boolean` 來說是 `false`，`char` 是 `'\0'`(空字元，印出來不會有東西)
        - **Exp**
            ```java
            class Data {
                char c;
                int i;
                float f;
                boolean b;

                public void print() {
                    System.out.println(c);
                    System.out.println(i);
                    System.out.println(f);
                    System.out.println(b);
                }
            }

            class Main {
                public static void main(String[] args) {
                    Data d = new Data();
                    d.print();
                }
            }
            ```
            **Output**
            ```

            0
            0.0
            false
            ```
    - **Class** (`Scanner`, `String`, `Integer`, ...): 設為 null
        - **Exp**
            ```java
            import java.util.Scanner;

            class Data {
                String str;
                Scanner sc;
                Integer i;

                public void print() {
                    System.out.println(str);
                    System.out.println(sc);
                    System.out.println(i);
                }
            }

            class Main {
                public static void main(String[] args) {
                    Data d = new Data();
                    d.print();
                }
            }
            ```
            **Output**
            ```
            null
            null
            null
            ```

<br>

- **如果有自己定義的、有參數的 constructor** (非 default constructor)，那 compiler 就**不會**自動產生 default constructor
    :::danger
    - **Exp**
        ```java=
        class Data {
            int a, b;

            public Data(int x, int y) {
                a = x;
                b = y;
            }

            public void print() {
                System.out.printf("(%d, %d)\r\n", a, b);
            }
        }

        class Main {
            public static void main(String[] args) {
                Data d = new Data();
                d.print();
            }
        }
        ```
    - **Error Message**
        ```
        Data.java:16: error: constructor Data in class Data cannot be applied to given types;
        Data d = new Data();
                 ^
          required: int,int
          found:    no arguments
          reason: actual and formal argument lists differ in length
        1 error
        ```
    :::

## 子類別的 Constructor

- 子類別**一定要**呼叫父類別的 constructor
- 可以自己透過 `super()` 呼叫父類別的 constructor
- `super()` 的引數 (arguments) 沒有限制，只要能對應父類別 constructor 的參數 (parameters) 就可以
    - **Exp**
        ```java=
        class Rectangle {
            int length, width;

            public Rectangle() {
                length = 0;
                width = 0;
            }

            public Rectangle(int l, int w) {
                length = l;
                width = w;
            }

            public int getArea() {
                return length * width;
            }
        }

        class Cuboid extends Rectangle {
            int height;

            public Cuboid() {
                super();      // 呼叫第 4 行的 Rectangle()
                height = 0;
            }

            public Cuboid(int l, int w, int h) {
                super(l, w);  // 呼叫第 9 行的 Rectangle(int, int)
                height = h;
            }

            public int getVolume() {
                return getArea() * height;
            }
        }

        class Main {
            public static void main(String[] args) {
                Cuboid c1 = new Cuboid();
                Cuboid c2 = new Cuboid(1, 2, 3);

                System.out.println("c1 base area: " + c1.getArea());
                System.out.println("c1 volume: " + c1.getVolume());
                System.out.println("c2 base area: " + c2.getArea());
                System.out.println("c2 volume: " + c2.getVolume());
            }
        }
        ```
        **Output**:
        ```
        c1 base area: 0
        c1 volume: 0
        c2 base area: 2
        c2 volume: 6
        ```

<br>

- 如果在子類別中**沒有自己呼叫 `super()`**，編譯器會自動幫你呼叫
- 自動呼叫的 `super()` 不會有引數，所以會呼叫父類別的 default constructor
- 會這樣設計，是因為編譯器沒有強大到可以判斷正確的引數
    - **Exp**
        ```java
        class Rectangle {
            int length, width;

            public Rectangle() {
                length = 0;
                width = 0;
            }

            public Rectangle(int l, int w) {
                length = l;
                width = w;
            }

            public int getArea() {
                return length * width;
            }
        }

        class Cuboid extends Rectangle {
            int height;

            public Cuboid() {
                // 不呼叫 super()
                // 但得到一樣的結果
                // 表示實際上 Rectangle() 有被執行
                height = 0;
            }

            public Cuboid(int l, int w, int h) {
                // 不呼叫 super(int, int)
                // getArea(), getVolume() 都得到 0
                // 表示實際上被執行的是 Rectangle()
                height = h;
            }

            public int getVolume() {
                return getArea() * height;
            }
        }

        class Main {
            public static void main(String[] args) {
                Cuboid c1 = new Cuboid();
                Cuboid c2 = new Cuboid(1, 2, 3);

                System.out.println("c1 base area: " + c1.getArea());
                System.out.println("c1 volume: " + c1.getVolume());
                System.out.println("c2 base area: " + c2.getArea());
                System.out.println("c2 volume: " + c2.getVolume());
            }
        }
        ```
        **Output**
        ```
        c1 base area: 0
        c1 volume: 0   
        c2 base area: 0
        c2 volume: 0
        ```

### 可能發生的錯誤

:::danger
以下的程式碼會編譯錯誤
```java
class Rectangle {
    int length, width;

    public Rectangle(int l, int w) {
        length = l;
        width = w;
    }

    public int getArea() {
        return length * width;
    }
}

class Cuboid extends Rectangle {
    int height;

    public Cuboid(int l, int w, int h) {
        length = l;
        width = w;
        height = h;
    }

    public int getVolume() {
        return getArea() * height;
    }
}

class Main {
    public static void main(String[] args) {
        Cuboid c = new Cuboid(1, 2, 3);

        System.out.println("c1 base area: " + c.getArea());
        System.out.println("c1 volume: " + c.getVolume());
    }
}
```

```
Data.java:17: error: constructor Rectangle in class Rectangle cannot be applied to given types;
    public Cuboid(int l, int w, int h) {
                                       ^
  required: int,int
  found:    no arguments
  reason: actual and formal argument lists differ in length
1 error
```
:::

**因為**...
- class `Rectangle` 中只有一個**有參數**的 constructor - `Rectangle(int, int)`
    - $\Rightarrow$ 因為有自訂的了，所以編譯器不會自動產生 default constructor - `Rectangle()`
    - $\Rightarrow$ 所以 `Rectangle()` 不存在
- `Cuboid(int, int, int)` 中沒有 `super()`
    - $\Rightarrow$ 編譯器會自動呼叫 `Rectangle()`
    - $\Rightarrow$ 但 `Rectangle()` 不存在，所以編譯錯誤

### 修正方案

- **手動呼叫 `super()`**
    - 因為 `Rectangle` 只有 `Rectangle(int, int)` 一個 constructor，所以在 `Cuboid()` 中要傳遞兩個引數給 `super()`
    <br>
    ```java
    class Rectangle {
        int length, width;

        public Rectangle(int l, int w) {
            length = l;
            width = w;
        }

        public int getArea() {
            return length * width;
        }
    }

    class Cuboid extends Rectangle {
        int height;

        public Cuboid(int l, int w, int h) {
            super(l, w);
            height = h;
        }

        public int getVolume() {
            return getArea() * height;
        }
    }

    class Main {
        public static void main(String[] args) {
            Cuboid c = new Cuboid(1, 2, 3);

            System.out.println("c1 base area: " + c.getArea());
            System.out.println("c1 volume: " + c.getVolume());
        }
    }
    ```

- **實作 default constrcutor**
    - 在 `Rectangle` 加入 default constrcutor - `Rectangle()`
        <br>
    ```java
    class Rectangle {
        int length, width;

        public Rectangle() {
            length = 0;
            width = 0;
        }

        public Rectangle(int l, int w) {
            length = l;
            width = w;
        }

        public int getArea() {
            return length * width;
        }
    }

    class Cuboid extends Rectangle {
        int height;

        public Cuboid(int l, int w, int h) {
            height = h;
        }

        public int getVolume() {
            return getArea() * height;
        }
    }

    class Main {
        public static void main(String[] args) {
            Cuboid c = new Cuboid(1, 2, 3);

            System.out.println("c1 base area: " + c.getArea());
            System.out.println("c1 volume: " + c.getVolume());
        }
    }
    ```

- **我全都要**
    - **最推薦的方式**
    - 如果情況允許，請盡量實作 default constructor
    - 並在所有子類別都呼叫 `super()`，且盡可能的把事情丟給 `super()` 做
        - 在此例中，`Rectangle` 有 `length`, `width` 共兩個 properties
        
        - `Cuboid` 除了繼承來的兩個，還有 `height` 共三個 properties
        
        - 在 `Cuboid()` 中，`length` 和 `width` 應該盡量由 `Rectangle()` 負責初始化
        
        - 因為 `Cuboid` 作為 `Rectangle` 的擴充，它新增了 `height`，那 `Cuboid` 就應該只負責 `height`，剩下的應該交給已經定義好的 `Rectangle`
    - 當然這只是建議，這個原則是可以依照需求打破的，能正確的完成需求才是優先考慮的事情

- **移除 `Rectangle` 的所有 constructor**
    - 在這個例子中，因為不會宣告 `Rectangle` 的物件，所以 `Rectangle` 可以不用有 constructor
    - 這樣編譯器就會自動產生 `Rectangle()`
    - 在 `Cuboid()` 中也可以不用 `super()`
    - 但不是每次都可以這樣設計，請小心使用

