Java | 1 日 120 分 × 7 日アプリ学習 中級編:オブジェクト指向(OOP) - 継承アプリ

Web APP Java
スポンサーリンク

1日目のゴール

継承アプリ1日目のテーマは
「共通部分をまとめる」=継承の入口を“安全に”体験すること です。

今日は、
「継承って便利そうだけど、どこが嬉しくて、どこが危険なのか」
を、まず“共通部分をまとめる”という一番シンプルなところから
かみ砕いて感じてもらいます。


継承って、そもそも何をする仕組み?

「似たクラスの共通部分を、親クラスにまとめる」

継承(extends)は、ざっくり言うとこういう仕組みです。

似たようなクラスがいくつもあるとき、
その「共通している部分」を親クラスにまとめて、
子クラスから再利用できるようにする。

イメージで言うと、

「犬」「猫」「鳥」みたいなクラスがあって、
どれも「名前」と「年齢」を持っているなら、
それを「動物」という親クラスにまとめる。

コードで見ると、こんな感じです。

public class Animal {
    protected String name;
    protected int age;

    public void introduce() {
        System.out.println("名前: " + name + ", 年齢: " + age);
    }
}
Java
public class Dog extends Animal {
    public void bark() {
        System.out.println("ワン!");
    }
}
Java
public class Cat extends Animal {
    public void meow() {
        System.out.println("ニャー");
    }
}
Java

DogCat は、
Animal の「名前」「年齢」「自己紹介メソッド」を
そのまま使えます。

「共通部分を親にまとめて、子から使う」
これが継承の基本の形です。


まずは“継承なし”で書いてみる

同じようなコードが増えていく感覚をあえて味わう

継承のありがたみを感じるには、
まず「継承なし」で書いてみるのが一番です。

例えば、「社員」と「アルバイト」を表すクラスを考えます。

public class Employee {
    private String name;
    private int age;
    private String employeeId;

    public Employee(String name, int age, String employeeId) {
        this.name = name;
        this.age = age;
        this.employeeId = employeeId;
    }

    public void introduce() {
        System.out.println("社員: " + name + " (" + age + "歳, ID: " + employeeId + ")");
    }
}
Java
public class PartTimer {
    private String name;
    private int age;
    private String partTimerId;

    public PartTimer(String name, int age, String partTimerId) {
        this.name = name;
        this.age = age;
        this.partTimerId = partTimerId;
    }

    public void introduce() {
        System.out.println("アルバイト: " + name + " (" + age + "歳, ID: " + partTimerId + ")");
    }
}
Java

ここで感じてほしいのは、

名前と年齢のフィールドがほぼ同じ
コンストラクタの前半もほぼ同じ
introduce() のロジックもかなり似ている

という 「似たコードが増えていく感じ」 です。

このまま増やしていくと、

契約社員
派遣社員
インターン

などが増えるたびに、
同じようなフィールドとメソッドを
何度も書くことになります。


共通部分を見つける:何が“同じ顔”をしているか

「人としての情報」と「雇用形態としての情報」を分ける

ここで、一歩立ち止まって
「何が共通で、何が違うのか」を言葉にしてみます。

共通しているもの:

名前
年齢

違っているもの:

社員ID
アルバイトID
表示文言(社員かアルバイトか)

つまり、

「人としての情報」と
「雇用形態としての情報」が混ざっている」

状態です。

この「人としての共通部分」を
親クラスにまとめるのが、
継承の一番素直な使い方です。


継承で“人としての共通部分”をまとめる

Person を親クラスにして、Employee / PartTimer を子にする

まず、共通部分だけを持つ Person を作ります。

public class Person {
    protected String name;
    protected int age;

    public Person(String name, int age) {
        if (age < 0) {
            throw new IllegalArgumentException("年齢は0以上");
        }
        this.name = name;
        this.age = age;
    }

    public void introduce() {
        System.out.println("名前: " + name + " (" + age + "歳)");
    }
}
Java

ここでは、

名前と年齢のフィールド
年齢のチェック
自己紹介の基本形

をまとめています。

次に、Employee を継承で書き直します。

public class Employee extends Person {
    private String employeeId;

    public Employee(String name, int age, String employeeId) {
        super(name, age);  // Person のコンストラクタを呼ぶ
        this.employeeId = employeeId;
    }

    @Override
    public void introduce() {
        System.out.println("社員: " + name + " (" + age + "歳, ID: " + employeeId + ")");
    }
}
Java

PartTimer も同様です。

public class PartTimer extends Person {
    private String partTimerId;

    public PartTimer(String name, int age, String partTimerId) {
        super(name, age);
        this.partTimerId = partTimerId;
    }

    @Override
    public void introduce() {
        System.out.println("アルバイト: " + name + " (" + age + "歳, ID: " + partTimerId + ")");
    }
}
Java

ここで起きていることを整理すると、

名前と年齢のフィールドは Person に移った
年齢チェックも Person に移った
Employee / PartTimer は「雇用形態としての違い」だけを持つようになった

つまり、
「共通部分を親にまとめて、違いだけを子に残す」
という継承の基本形ができています。


重要ポイントの深掘り1:super と「親の責務」

親に任せるところと、子で上書きするところ

継承でよく出てくるキーワードが super です。

super(name, age); は、
「親クラスのコンストラクタを呼ぶ」 という意味です。

ここで大事なのは、

「名前と年齢の初期化」と
「年齢のチェック」という責務を
Person に任せていること。

Employee や PartTimer は、
「自分の ID をどう持つか」だけに集中できる。

さらに、@Override
introduce() を上書きしているところもポイントです。

親の introduce() は「基本形」。
子の introduce() は「雇用形態ごとの具体形」。

「共通の振る舞いは親に置き、
必要なら子で上書きする」

これが継承のもう一つの顔です。


重要ポイントの深掘り2:継承は“なんでもまとめる魔法”ではない

「共通しているから親にする」は危険なときもある

ここで、継承の“危険な側面”も少し触れておきます。

継承は便利ですが、
「共通しているからとりあえず親にまとめる」
という使い方をすると、すぐに苦しくなります。

例えば、

「社員」と「商品」が
どちらも「名前」を持っているからといって、
同じ親クラスにしてしまうのは変です。

共通しているのは「フィールド名」だけで、
意味はまったく違うからです。

継承でまとめていいのは、

「同じ種類のもの」
「同じ世界の中のもの」

だけです。

Employee と PartTimer は
「人間であり、雇用形態が違う」という関係なので
Person を親にするのは自然です。

でも、
User と File と Order を
「全部 ID を持っているから」といって
同じ親にするのは不自然です。

「共通している“意味”があるか?」
を見てから、継承を使うかどうか決める。

これが、継承を安全に使うための大事な視点です。


例題:通知クラスで“共通部分をまとめる”

メール通知とプッシュ通知の共通部分

もう一つ、継承の基本形を感じる例を出します。

「通知」を表すクラスを考えます。

メール通知:

宛先メールアドレス
件名
本文

プッシュ通知:

宛先ユーザーID
タイトル
本文

まず、継承なしで書いてみます。

public class MailNotification {
    private String toAddress;
    private String subject;
    private String body;

    public void send() {
        System.out.println("メール送信: " + toAddress + " / " + subject);
    }
}
Java
public class PushNotification {
    private String userId;
    private String title;
    private String body;

    public void send() {
        System.out.println("プッシュ送信: " + userId + " / " + title);
    }
}
Java

ここで共通部分を探します。

「通知」という意味で共通しているもの:

本文(body)
送るという行為(send)

違っているもの:

宛先の種類(メールアドレスかユーザーIDか)
表示文言

そこで、親クラス Notification を作ります。

public abstract class Notification {
    protected String body;

    public Notification(String body) {
        this.body = body;
    }

    public abstract void send();
}
Java

MailNotification はこう。

public class MailNotification extends Notification {
    private String toAddress;
    private String subject;

    public MailNotification(String toAddress, String subject, String body) {
        super(body);
        this.toAddress = toAddress;
        this.subject = subject;
    }

    @Override
    public void send() {
        System.out.println("メール送信: " + toAddress + " / " + subject);
        System.out.println("本文: " + body);
    }
}
Java

PushNotification はこう。

public class PushNotification extends Notification {
    private String userId;
    private String title;

    public PushNotification(String userId, String title, String body) {
        super(body);
        this.userId = userId;
        this.title = title;
    }

    @Override
    public void send() {
        System.out.println("プッシュ送信: " + userId + " / " + title);
        System.out.println("本文: " + body);
    }
}
Java

ここでも、

「通知としての共通部分(本文と send という行為)」を親にまとめて、
「通知の種類ごとの違い」を子に残す。

という継承の基本形が見えます。


1日目で本当に掴んでほしいこと

継承アプリ1日目で伝えたいのは、
「継承は“共通部分をまとめる道具”だけど、
まとめるのは“意味が共通している部分”だけ」

という感覚です。

似たクラスが増えてきたときに、

何が同じで、何が違うのか
同じ部分は「同じ世界のもの」か
それを親クラスにまとめると、読みやすく・直しやすくなるか

を考えてから、
extends を使う。

「共通部分をまとめる」は、
継承の入り口として一番分かりやすいテーマですが、
同時に「意味を見てからまとめる」という
設計の目も育ててくれます。

次のステップでは、
この「共通部分をまとめる」から
「共通のインターフェースを持つ」「多態性(ポリモーフィズム)」
へとつなげていけます。

タイトルとURLをコピーしました