アットウィキロゴ
Javaプログラミング入門
掲示板 掲示板 ページ検索 ページ検索 メニュー メニュー

Javaプログラミング入門

18. 実装による継承

最終更新:

javatutorial

- view
管理者のみ編集可

これまでの不便な点と「実装」のメリット

前回学んだ「拡張による継承(extends)」や「抽象クラス」を使うと、親の機能を引き継いだり、子クラスにルールの作成を強制したりできることを学びました。
しかし、これらの仕組みを使っても、どうしても解決できない「1つの大きな制限」があります。

それは、「親クラスは1つしか選べない(多重継承の禁止)」という絶対のルールです。

例えば、「電話(Phone)」という親クラスと、「カメラ(Camera)」という親クラスがあったとします。この2つの機能を両方とも引き継いだ「スマートフォン(SmartPhone)」を作りたいとき、Javaでは1つの子クラスに対して2つ以上の親を同時にextendsで継承することはできません。
親クラス(抽象クラスを含む)は、具体的なデータや処理の中身(変数や普通のメソッド)を持つことができるため、複数の親から同時に引き継ぐと「親同士で機能がぶつかったとき、どちらを使えばいいのか?」とコンピュータが混乱してしまうからです。

「じゃあ、複数の機能を持ったクラスはどうやって作ればいいの?」という悩みを解決するのが、今回学ぶインターフェースを使った実装による継承です。

インターフェースは、具体的なデータや処理の中身を一切持たず、「どんな名前のメソッドを作るべきか」というルール(設計図)だけをまとめた「100%純粋なルールブック」です。
中身が完全にカラッポであるため、複数の親から引き継いでもコンピュータが混乱する心配がなく、いくつでも同時に組み合わせて使う(実装する)ことができるのです。

インターフェースとは?

インターフェースは、具体的な処理(メソッドの中身)を一切持たず、「どんな名前のメソッドを作るべきか」というルール(設計図)だけをまとめたものです。
抽象クラスでも出てきましたが、インターフェースの中に書く「中身がカラッポのメソッド」のことを、抽象メソッドと呼びます。

実装による継承の基本構文

インターフェースは class の代わりに interface(インターフェース) というキーワードを使って作ります。
そして、そのルールを守ってクラスを作るときは、extends ではなく implements を使います。
implementsには「実装する(ルール通りに機能を中身まで作る)」という意味があります。

// ルールブックの作成
interface ルールブック名 {
    public 戻り値の型 メソッド名(); // { } は書かず、最後に ; をつける
}
 
// ルールブックに従ってクラスを作成
class クラス名 implements ルールブック名 {
    // ルールブックに書かれたメソッドの中身({ } の中)を必ず書く
} 

複数のインターフェースを同時に実装する

extends(拡張による継承)は1つしかできませんでしたが、implements(実装による継承)は、カンマ( , )で区切ることでいくつでも同時に実装することができます。
class クラス名 implements ルールA, ルールB { ... } 

実際にプログラムを書いてみよう

スマートフォンの例を使って、実際にインターフェースを実装してみましょう。
まず、「写真を撮る」というルールを持ったカメラと、「電話をかける」というルールを持った電話のインターフェースを作ります。

サンプル:Camera.java
  1. public interface Camera {
  2. // 中身のない抽象メソッド(ルール)
  3. public void takePhoto();
  4. }
サンプル:Phone.java
  1. public interface Phone {
  2. // 中身のない抽象メソッド(ルール)
  3. public void call();
  4. }

次に、この2つのインターフェースを両方とも実装(implements)したスマートフォンクラスを作ります。

サンプル:SmartPhone.java
  1. // CameraとPhoneの両方のルールを実装する
  2. public class SmartPhone implements Camera, Phone {
  3. // Cameraのルールを守るため、takePhotoの中身を書く(オーバーライド)
  4. @Override
  5. public void takePhoto() {
  6. System.out.println("カシャッ!高画質な写真を撮りました。");
  7. }
  8.  
  9. // Phoneのルールを守るため、callの中身を書く(オーバーライド)
  10. @Override
  11. public void call() {
  12. System.out.println("プルルル…電話をかけています。");
  13. }
  14. }

最後に、メインのプログラムから使ってみましょう。

サンプル:Main.java
  1. public class Main {
  2. public static void main(String[] args) {
  3. SmartPhone sp = new SmartPhone();
  4.  
  5. // 実装したメソッドを呼び出す
  6. sp.takePhoto();
  7. sp.call();
  8. }
  9. }
実行結果
カシャッ!高画質な写真を撮りました。
プルルル…電話をかけています。 

💡注意!
implements を使った場合、インターフェースに書かれているメソッドの中身をすべて必ず書かなければなりません。もし、SmartPhoneクラスの中で takePhoto() や call() を書き忘れると、コンパイルエラー(プログラムの文法間違い)として怒られてしまいます。
この「書き忘れを許さない」という厳しさ(ルールの強制力)が、多人数で大きなプログラムを作る際に、機能のもれを防ぐ大きなメリットになります。

継承の種類のまとめ
種類 キーワード 親の数 特徴
拡張による継承 extends 1つだけ 親の中身が完成しているメソッドをそのまま引き継ぐ。
実装による継承 implements 複数OK カラッポのルールの名前だけを引き継ぎ、中身は必ず自分で書く。

コラム

インターフェースのメソッドはなぜ自動的にpublicになる?

インターフェースの中に抽象メソッドを書く際、先頭にpublicを付けなくてもエラーにはなりません。
実は、Javaの言語仕様(コンパイラの裏側のルール)として、インターフェース内に書かれたメソッドは、自動的にpublic abstractどこからでもアクセスできる、中身のない抽象的なもの)という飾りが隠れて付与されるようになっています。
インターフェースはそもそも「このクラスは外からこういう操作ができますよ」という「外部との約束事(公開される操作のリスト)」を定義するためのものです。

インターフェース同士の「多重継承」

本編で「クラスは複数のインターフェースを同時に実装(implements)できる」と学びましたが、実はインターフェース自身も、別の複数のインターフェースを継承(extends)して、さらに大きなルールブックを作ることができます。

// 2つの別々のルールブック
interface Camera {
    public void takePhoto();
}
interface Phone {
    public void call();
}
 
// 2つのルールを両方とも引き継いだ、新しいルールブック(多重継承)
interface SmartPhone extends Camera, Phone {
    public void installApp(); // スマホ独自の新しいルールを追加
} 
クラスの継承では絶対に禁止されていた「多重継承(カンマで区切って複数の親を持つこと)」が、なぜインターフェースでは許されているのでしょうか?
理由はとてもシンプルで、「中身(具体的な処理)が空っぽだから」です。

クラスの場合、2つの親が同じ名前のメソッドを持っていて、それぞれ中身の動きが違った場合、子クラスは「どちらの動きを引き継げばいいの?」とパニックになってしまいます。
しかし、インターフェースの場合は「この名前の機能を作ってください」という名前(ルール)だけしか持っていません。
そのため、もし複数の親から同じルールの指定を重ねて受けたとしても、最終的に子クラス(それを実装するクラス)の中で処理の中身を1つだけ作れば済むため、コンピュータが混乱することがないのです。
この安全な仕組み(仕様)のおかげで、Javaではインターフェース同士を使った多重継承が特別に認められています。
(Java SE 17 Programmer I (1Z0-825-JPN) 試験になるともっと複雑な継承関係の問題が多数出題されるので興味ある方は挑戦してみてください)

インターフェースの進化:定数と中身のある特別なメソッド

これまで「インターフェースの中身は100%カラッポのルールだけ」と説明しましたが、実はJavaがバージョンアップして進化していく中で、プログラマーを助けるための便利な機能が少しだけ追加されてきました。
それが「定数」と「中身のある特別なメソッド」です。

① 変数は持てないが「定数」は持てる
インターフェースは具体的なデータ(中身が変わる変数の箱)を持つことはできません。
しかし、絶対に中身が変わらない「定数」だけは特別に持たせることができます。
インターフェースの中に変数を書くと、自動的に public static final (どこからでも使える、書き換え不可能な共通のデータ)という飾りが隠れて付きます。

interface Camera {
    // 自動的に public static final int MAX_ZOOM = 10; として扱われる
    int MAX_ZOOM = 10;
 
    public void takePhoto();
} 
② 共通の動きを用意できる「default(デフォルト)メソッド」
「ルールブックを作ったけれど、どのクラスでも全く同じ処理をわざわざ書いているな…」という場合のために、default(デフォルト) というキーワードを使うと、特別にインターフェースの中に「あらかじめ中身が作られたメソッド」を書くことができます。
これを書いておけば、子クラスで毎回オーバーライド(上書き)して中身を作らなくても、そのままその動きを使うことができます(もちろん、子クラス側で自分好みに上書きすることも可能です)。

③ インターフェース内の秘密の処理「private(プライベート)メソッド」
さらに、複数のdefaultメソッドの中で「同じような処理」が何度も出てきた場合、それを一つにまとめるための private メソッドも作れるようになりました。
これはインターフェースの内部だけで使う「秘密のお手伝いメソッド」なので、このルールブックを実装する子クラスや、外側のプログラムからは一切見えません。

これらをすべて使ったプログラムを見てみましょう。

サンプル:SmartDevice.java
  1. public interface SmartDevice {
  2. // ① 定数(public static final が隠れている)
  3. String VERSION = "1.0";
  4.  
  5. // 中身のない通常のルール(抽象メソッド)
  6. public void turnOn();
  7.  
  8. // ② 中身のある default メソッド
  9. default public void connectWiFi() {
  10. System.out.println("Wi-Fiに接続します。");
  11. checkSecurity(); // ③ privateメソッドを呼び出して手伝ってもらう
  12. }
  13.  
  14. // ③ インターフェースの中だけで使う private メソッド
  15. private void checkSecurity() {
  16. System.out.println("安全性を確認しました。");
  17. }
  18. }
このように、現在のインターフェースは「空のルールブック」という基本ルールを守りつつも、プログラマーがより楽に、そして安全に開発できるように進化を続けているのです。

最近更新されたスレッド
人気記事ランキング
ウィキ募集バナー