第7回 でクラスとオブジェクトを学びました。今回はオブジェクト指向の真打ちである 「継承(けいしょう)」 と 「インターフェース」 を学びます。
「同じようなクラスを何個も書くのが面倒…」── 継承を使えば、既存のクラスを土台にして新しいクラスを作ることができ、コードの重複を一気に減らせます。
この記事のゴール
「継承(extends)とは?」「インターフェース(implements)とは?」「@Override の意味」「ポリモーフィズムって何?」が理解できることです。難しそうな言葉ですが、1つずつ噛み砕いていきます!
継承とは?(ざっくり 30 秒で)
継承 とは、既存のクラス(親)を そのまま受け継いで、新しいクラス(子)を作る仕組みです。
💡 イメージ: 親クラスを 「基本セット」 として、子クラスで 「カスタマイズ」 していく感じ。
たとえば「動物」クラスを作り、それを継承して「犬」「猫」「鳥」を作れば、共通の特徴(名前、年齢、鳴く)は親クラスに 1 度だけ書けば OK。子クラスは 「鳥は飛べる」などの独自の特徴だけ追加すればよくなります。
継承の基本: extends
親クラス(基本セット)
// Animal.java(親クラス)
public class Animal {
String name; // 名前
int age; // 年齢
// コンストラクタ
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
// 全ての動物が持つメソッド
public void introduce() {
System.out.println(name + "(" + age + "歳)です。");
}
}
子クラス(extends で継承)
// Dog.java(子クラス)
// ↓ extends で親クラス Animal を継承
public class Dog extends Animal {
// 子クラスのコンストラクタ
public Dog(String name, int age) {
super(name, age); // ← 親のコンストラクタを呼ぶ(必須)
}
// 犬だけの独自メソッド(親にはない)
public void bark() {
System.out.println(name + "「ワンワン!」");
}
}
重要ポイント:
extends Animalで「Animal を継承する」と宣言super(name, age)で親のコンストラクタを呼ぶ(子クラスのコンストラクタの最初に書く)- 親の name / age / introduce() が自動で使える状態になる
使ってみる
public class Main {
public static void main(String[] args) {
Dog pochi = new Dog("ポチ", 3);
pochi.introduce(); // ← 親クラスのメソッドが使える!→ ポチ(3歳)です。
pochi.bark(); // ← 子クラスのメソッド → ポチ「ワンワン!」
}
}
1 行も書いていない introduce() が使えるのが、継承の強みです。
メソッドのオーバーライド: @Override
「親のメソッドの中身を 子クラスで書き換えたい」── そんなときは オーバーライド(上書き) します。
public class Cat extends Animal {
public Cat(String name, int age) {
super(name, age);
}
// 親の introduce() を上書き(書き換え)
@Override // ← この目印を付けると安全
public void introduce() {
System.out.println(name + "は猫。気まぐれです。");
}
}
Cat tama = new Cat("タマ", 2);
tama.introduce(); // → タマは猫。気まぐれです。(親の introduce() ではなく子が呼ばれる)
💡
@Overrideの意味: 「これは親のメソッドを上書きするつもりだよ」というコンパイラへの宣言。スペルミスなどで上書きできていないと自動でエラーになるので、必ず付ける習慣にしましょう。
インターフェースとは?
インターフェース は、「このクラスはこういう機能を持つ」という約束を定義する仕組みです。
💡 イメージ: クラスが 「設計図」 なら、インターフェースは 「契約書」。 「電話できる」「メッセージ送れる」と書かれた契約書(インターフェース)に、スマホ・ガラケー・固定電話などの 異なるクラス がサインする感じです。
インターフェースの宣言
// Flyable.java(インターフェース)
// ↓ class ではなく interface と書く
public interface Flyable {
// メソッドの「約束」だけを書く(中身は書かない)
void fly();
}
インターフェースを実装するクラス
// Bird.java
// ↓ extends ではなく implements(実装する)
public class Bird extends Animal implements Flyable {
public Bird(String name, int age) {
super(name, age);
}
// インターフェースで約束した fly() の中身を書く(必須)
@Override
public void fly() {
System.out.println(name + "は空を飛ぶ!");
}
}
ポイント:
implements Flyableで「Flyable を実装します」と宣言- インターフェースで定義されたメソッドは 必ず子クラスで実装しなければならない(でないとコンパイルエラー)
- 継承(extends)とインターフェース(implements)は同時に使える
継承とインターフェースの違い
| 観点 | 継承(extends) | インターフェース(implements) |
|---|---|---|
| 何を継承する? | 「実体(フィールド + メソッド)」 | 「約束(メソッドの宣言だけ)」 |
| 多重継承 | ❌ 1 つの親しか継承できない | ⭕ 複数実装できる |
| キーワード | extends | implements |
| 使う場面 | 「is-a」関係(犬は動物だ) | 「can-do」関係(犬は走れる) |
複数のインターフェースを同時実装
public class Duck extends Animal implements Flyable, Swimmable {
// Flyable と Swimmable を両方実装する場合、両方のメソッドを書く
@Override
public void fly() {
System.out.println(name + "は飛ぶ!");
}
@Override
public void swim() {
System.out.println(name + "は泳ぐ!");
}
}
ポリモーフィズム(多態性)
ポリモーフィズム(多態性) ── 名前は長いですが、要は 「親の型で、子のオブジェクトを扱える」 ということです。
// 親の型 (Animal) で、子のオブジェクト (Dog, Cat, Bird) を扱える!
Animal[] animals = {
new Dog("ポチ", 3),
new Cat("タマ", 2),
new Bird("ピーコ", 1)
};
// 同じ for 文ですべての動物を introduce
for (Animal a : animals) {
a.introduce();
// → ポチ(3歳)です。
// → タマは猫。気まぐれです。 ← Cat の introduce が呼ばれる(オーバーライド済み)
// → ピーコ(1歳)です。
}
何が嬉しいか?: 新しい動物クラス(Rabbit など)を追加しても、main の for 文は一切変えなくていい。これがオブジェクト指向の真骨頂です。
編集部からのポイント
ポリモーフィズムは初心者には難しく感じますが、「親の型を箱として使うと、中身を入れ替えても外側のコードは変わらない」と覚えれば OK。フレームワーク(Spring など)はこの仕組みでできています。
ハマりがちな落とし穴
落とし穴①: super() を忘れる
public Dog(String name, int age) {
// super() を書かないと、コンパイラが自動で super() を入れる
// でも親に引数なしコンストラクタがないとエラー!
}
対処: 子クラスのコンストラクタの 最初の行 で super(...) を必ず書く習慣を。
落とし穴②: private なフィールドにアクセスできない
public class Animal {
private String name; // private は外から見えない
}
public class Dog extends Animal {
public void show() {
System.out.println(name); // ❌ コンパイルエラー
}
}
対処: 親で protected(継承先からは見える)にするか、ゲッターメソッド を作って呼ぶ。
落とし穴③: 親と子で同じフィールド名を持つ
public class Parent { int value = 10; }
public class Child extends Parent { int value = 20; }
対処: 基本的に避けましょう。混乱の元です。
練習問題
「動物(Animal)を継承した 犬(Dog)と 猫(Cat)」を作り、それぞれに makeSound() メソッドをオーバーライドで実装してください。
解答例
// Animal.java
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void makeSound() {
System.out.println(name + "が鳴いた");
}
}
// Dog.java
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + "「ワンワン!」");
}
}
// Cat.java
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + "「ニャー」");
}
}
// Main.java
public class Main {
public static void main(String[] args) {
// Animal[] に犬と猫を入れる(ポリモーフィズム!)
Animal[] animals = { new Dog("ポチ"), new Cat("タマ") };
for (Animal a : animals) {
a.makeSound(); // 中身に応じて Dog or Cat の makeSound が呼ばれる
}
// → ポチ「ワンワン!」
// → タマ「ニャー」
}
}
まとめ
第 10 回では、以下を学びました。
- ✅ 継承(extends) は親クラスを土台にして子クラスを作る仕組み
- ✅
super(...)で親のコンストラクタを呼び出す - ✅
@Overrideで親のメソッドを上書き(オーバーライド) - ✅ インターフェース(implements) は「約束」を定義する仕組み
- ✅ クラスは 1 つだけ継承可、インターフェースは複数実装可
- ✅ ポリモーフィズム で親型のまま子オブジェクトを扱える
次回は 「ファイル入出力」 を学びます。File / Scanner を使って、ファイルから読み込んだり書き込んだりする方法を解説します。お楽しみに!