【Java基礎】継承

# はじめに
最近Java基礎の研修を受けました。学生時代にJavaを授業で習って基礎は身につけていたのですが、忘れていた内容もあったので備忘録として書き残してみます。

この記事では、**Javaの基礎的な内容に関してまとめていきます**。
今回は**継承**です。


# 継承とは
**継承**とは、クラスの機能を受け継いで機能を拡張した新しいクラスを作成する方法です。
このとき、継承元となるクラスを**親クラス（スーパークラス）**、継承して新たに作成したクラスを**子クラス(サブクラス)** と呼びます。

次の関係を見てみましょう。
+ 商品
    + 本
    + 食材

このとき、「商品」という親クラスから、「本」「食材」という子クラスが作成されています。

## 継承の方法
継承は以下のように行います。
```java:継承の形式
class 子クラス extends 親クラス名 {
}
```

継承の例を見てみましょう。

```java:「商品」クラス
class Item {
    // 「商品」クラス
}
```

```java:「本」クラス
class Book extends Item {
    // 「本」クラス
}
```

```java:「食材」クラス
class Food extends Item {
    // 「食材」クラス
}
```

## 継承の注意
継承を利用するときは、以下の点に気をつけましょう。
1. 継承できるクラスは一つ
1. 継承したクラスをさらに継承できる
1. 親クラスから子クラスのメンバは参照できない

## (参考）Objectクラス
全てのクラスは暗黙的にObjectクラスを継承します。**全てのクラスの始祖はObjectクラス**なのです。

Objectクラスには、全てのクラスが持つべきメソッドが宣言されています。
* toStringメソッド
    * オブジェクトの文字列表現を返す
* equalsメソッド
    * 2つのインスタンスが等しいかどうかを示す

全てのクラスはtoStringメソッドもequalsメソッドも呼び出せます。



# オーバーライド
**オーバーライド**とは、子クラスで親クラスのメソッドを上書きすることです。

ObjectクラスのtoStringメソッドをオーバーライドしてみましょう。
```java:toStringのオーバーライド
class Item {
    private String name;
    private int price;

    public void setName(){ 省略 }

    public void setPrice(){ 省略 }

    @Override 
    public String toString() {
        return name + "(" + price + "円)";
    }
}
```

```java:実行例
public static void main(String[] args) {
    Item item = New Item();
    item.setName("Qiita攻略本");
    item.setPrice(1000);
    String str = item.toString();
    System.out.println(str);  // Qiita攻略本(1000円)
}
```

オーバーライドしたメソッドの中で`super.メソッド名(引数リスト）`と記述することで、親クラスのメソッドを呼び出すこともできます。



# 抽象メソッドと抽象クラス
オーバーライドを前提としたメソッドを**抽象メソッド**、抽象メソッドが宣言されているクラスを**抽象クラス**と呼びます。対比として、抽象メソッドを持たないクラスを**具象クラス**と呼ぶこともあります。

抽象クラスは以下の特徴があります。
* インスタンスを作成できない
* 抽象クラスは継承され、抽象メソッドをオーバーライドされる前提で作られる
* 抽象クラスを継承したクラスは抽象メソッドを全てオーバーライドしないとコンパイルエラーとなる


## 抽象クラスと抽象メソッドの宣言
抽象メソッドは、戻り値の型の前に`abstract`をつけて宣言します。また、処理の中身を持たないので`{}`の代わりに`;`を記述します。

抽象メソッドは以下のように記述します。
```java:抽象メソッドの形式
修飾子 abstract 戻り値の型 メソッド名（引数リスト）;
```

抽象クラスの抽象メソッドと同様に、`class`の前に`abstract`をつけて宣言します。抽象メソッド以外のメンバも宣言できます。
```java:抽象クラスの形式
修飾子 abstract class クラス名 {
    抽象メソッドなどのメンバ
}
```

抽象クラスと具象クラスの例を見てみましょう。
```java:抽象クラス
abstract class Page {
    public abstract String getTitle();
    
    public void printReview() {
        System.out.println("title : " + getTitle() );
        System.out.println("review : It was really great story.");
    }
}
```


```java:具象クラス
class ReviewPage extends Page {
    @Override 
    public abstract String getTitle() {
        return "The Story Of Qiita";
    }
}
```


```java:使用例
public static void main(String[] args) {
    ReviewPage page = New ReviewPage();
    page.printReview();
}
```

```:実行結果
title : The Story Of Qiita
review : It was really great story.
```



# コンストラクタ
具象クラスと抽象クラスのどちらを継承した場合も、**親クラスのコンストラクタは、子クラスに引き継がれません**。

```java:親クラス
class Item {
    private String name;
    private int price;

    public Item(String name, int price) {
        this.name = name;
        this.price = price;
    }
}
```

```java:子クラス
class Book extends Item {
    // メンバを宣言していない
}
```

```java:実行結果
public static void main(String[] args) {
    // Book book = new Book("Qiita攻略", 1000);  // コンパイルエラー
}
```

親クラスのコンストラクタを呼び出すには、子クラスのコンストラクタ内で`super(引数リスト)`と記述します。

```java:子クラス
class Book extends Item {
    public Book(String name, int price) {
        super(name, price);  // 親クラスのコンストラクタを呼び出す
    }
}
```

```java:実行結果
public static void main(String[] args) {
    Book book = new Book("Qiita攻略", 1000);  // コンパイルエラーは起こらない
}
```

通常、子クラスのコンストラクタは、**暗黙的に親クラスの引数なしコンストラクタを呼び出します**。親クラスに、引数1個以上のコンストラクタのみが宣言されている場合は、明示的に呼び出す必要があります。




# 継承したクラスのインスタンス
親クラスの型の変数には、子クラスのインスタンスを代入できます。
※逆は成り立ちません。
```java:インスタンスの例
Item book = new Book();
Page page = new ReviewPage();
```

親クラス型の変数に子クラスのインスタンスを代入するときは、呼び出せるメソッドと呼び出し結果に注意が必要です。なぜなら、**コンパイル時と実行時で、参照している型が異なる**からです。
* コンパイル時：変数の型を参照し、メソッドが実行できるかが決まる
* 実行時：インスタンスの型を参照し、メソッドの実行内容が決まる


# まとめ
初めのうちは、クラスにも親子関係が作れて、中身をアップデートできるんだな〜と思ってくれれば良いと思います。細かい制約がありますが、徐々に覚えていきましょう！