インタフェースの利用方法
2.4 インタフェースの利用方法
プログラムでどのように記述すれば、インタフェースを利用できるのかその方法について説明していきます。
2.4.1 インタフェースを定義する方法について
インタフェースも抽象クラス同様にオブジェクト化できない為、継承して利用する事が大前提になります。
まずはインタフェースを定義する基本構文を以下に示します。
以下にインタフェース定義する時のルールについて示します。
1. インタフェースの定義について
1)interfaceにはpublicやabstract修飾子が使用可能。
2)publicが省略された場合、同じパッケージ内からしかアクセスできない。
2. インタフェースのメンバ変数について
1)すべての「変数」は「public static final」が付いている定義と同じになる。
2)「public」「static」「final」修飾子のいずれか、もしくは全部を省略する事も可能。
3)定数となるため、初期値の設定が必要になる。
3. インタフェースのメソッドについて
1)全てのメソッドは「public abstract」が付いている定義と同じになる。
2)「public」「abstract」修飾子のいずれかもしくは全部を省略が可能。
3)static修飾子をつけることはできない。
4)メソッドが本体(処理)を持つことができない。
ポイント
- インタフェースを定義するにはclass修飾子の代わりにinterface修飾子を利用する。メンバはフィールド変数(定数)と抽象メソッドのみ定義できる。
凡例:インタフェースの実装
クラスにインタフェースを実装する場合は継承とは異なり「implements」で実装します。抽象メソッドと同様に抽象メソッドが定義されている場合は、必ずオーバーライドする必要があります。
上記凡例で示した2つのComputer2クラスとTelevision2クラスは、前述した凡例の抽象クラス「IMachinery(機械)」インタフェースを実装しています。インタフェースで行うことは抽象メソッドを必ずオーバーライドする事ぐらいしかないので、内容的には抽象クラスと同じになります。
ポイント
- インタフェースを実装する場合implements修飾子を利用する。抽象メソッドが定義されている場合、必ずサブクラス内でオーバーライドする必要がある。
では、次の項でインタフェースを実装した新しいクラスを利用するプログラムを紹介します。
2.4.2 インタフェースを実装した新しいクラスを利用するプログラム
インタフェースを実装した新しいクラスを作成し、そのクラスの機能を利用できることを確認します。
➢ IMachinery.java ※インタフェース(機械)① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch12
③ 名前 :IMachinery
package jp.co.f1.basic.ch12; public interface IMachinery { String MANUFACTURER = "Japan"; // 製造元 public void show(); // 抽象メソッド }
解説
このIMachineryインタフェースは3行目のinterface修飾子で、定義する事によりインタフェースになっています。
メンバには5行目で製造元情報を保持するフィールド変数MANUFACTURERを、7行目では抽象メソッドshow()の定義を行っています
➢ Computer2.java ※インタフェースIMachineryを実装したクラス① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch12
③ 名前 :Computer2
package jp.co.f1.basic.ch12; //インタフェースMachineryを実装 public class Computer2 implements IMachinery { private String os; private int memory; // コンストラクタ(引数あり) public Computer2(String os, int memory) { this.os = os; this.memory = memory; System.out.println("OS「" + os + "」メモリサイズ「" + memory + "MByte」のパソコンを作成しました。"); } // 抽象メソッドをオーバーライド public void show() { System.out.println("パソコンのOSは「" + os + "」です。"); System.out.println("メモリサイズは「" + memory + "MByte」です。"); System.out.println("製造元は「" + MANUFACTURER + "」です。"); } }
解説
4行目でインタフェースであるIMachineryを実装しています。
17行目でIMachineryインタフェースの抽象メソッドであるshow()メソッドをオーバーライドしています。
20行目ではインタフェースで定義されている、定数MANUFACTURERを参照し製造元情報を取得しています。
➢ Television2.java ※インタフェースIMachineryを実装したクラス① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch12
③ 名前 :Television2
package jp.co.f1.basic.ch12; //インタフェースMachineryを実装 public class Television2 implements IMachinery { private int screenSize; // 画面サイズ // 引数なしのコンストラクタ public Television2() { this.screenSize = 0; System.out.println("テレビを作成しました。"); } // アクセサメソッド public void setScreenSize(int screenSize) { this.screenSize = screenSize; System.out.println("画面サイズは" + screenSize + "型にしました。"); } // 抽象メソッドをオーバーライド public void show() { System.out.println("テレビの画面サイズは「" + this.screenSize + "型」です。"); System.out.println("製造元は「" + MANUFACTURER + "」です。"); } }
解説
4行目でインタフェースであるIMachineryを実装しています。
19行目でIMachineryインタフェースの抽象メソッドであるshow()メソッドをオーバーライドしています
21行目ではインタフェースで定義されている、定数MANUFACTURERを参照し製造元情報を取得しています。
下図を見てもらうと分かりますが、インタフェースを実装したクラスのイメージは抽象クラスと同じになります。
図 2.4.1: インタフェースをクラスに実装
➢ UseImplementsInterface.java ※mainメソッドを持つ実行クラス① ソース・フォルダー :myproj_basic/src
② パッケージ :jp.co.f1.basic.ch12
③ 名前 :UseImplementsInterface
④ 作成するメソッド・スタブの選択:public static void main(String[] args) にチェックを入れる
package jp.co.f1.basic.ch12; public class UseImplementsInterface { public static void main(String[] args) { // Computer2クラスをオブジェクト化 System.out.println("[Computer2オブジェクトの作成]"); Computer2 com = new Computer2("Windows7", 3072); System.out.println("---------------------------------------"); // Television2クラスをオブジェクト化 System.out.println("[Television2オブジェクトの作成]"); Television2 tv = new Television2(); tv.setScreenSize(42); System.out.println("---------------------------------------"); // ComputerとTelevisionオブジェクトの情報を表示 System.out.println("■Computer2情報を表示"); com.show(); System.out.println("■Television2情報を表示"); tv.show(); } }
実行結果
解説
7行目でComputer2クラスのオブジェクト化を引数ありのコンストラクタで行っています。
7行目の処理は実行結果を見て分かるように、正しく実行されているのが確認できます。
続いて13行目でTelevision2クラスのオブジェクトを引数なしのコンストラクタで行っています。14行目では自身のクラスのsetScreenSize()メソッドを呼び出して、画面サイズの値42を設定しています。
13、14行目の処理は実行結果を見て分かるように、正しく実行されているのが確認できます。
13、14行目の処理は実行結果を見て分かるように、正しく実行されているのが確認できます。
最後に18、20行目でインタフェースIMachineryの抽象メソッドを、オーバーライドしたshow()メソッドを呼び出して各オブジェクトの情報を表示しています。
18、20行目の処理は実行結果を見て分かるように、正しく各クラスでオーバーライドしたshow()メソッドが実行されているのが確認できます。
今回のプログラムではインタフェースを実装したクラスも、必ず抽象メソッドをオーバーライドして利用しています。これによりインタフェースも抽象クラスと同じような働きをもつものだと分かります。ただし、インタフェースのフィールドは全て定数で、メソッドは全て抽象メソッドとなっています。2.2.2項の抽象クラス(Machinery)のように、値を変更できるフィールド変数や、処理が定義されているメソッドのようなメンバは持たせることができないことを改めて覚えておいて下さい。
図 2.4.2: インタフェースで宣言した抽象メソッドのオーバーライド
ポイント
- サブクラスはスーパークラスのフィールドとメソッドを引き継ぎ、さらに機能を追加できる。
2.4.3 インタフェースと抽象クラスの比較
これまでの学習で抽象クラスとインタフェースについて学習してきました。しかしどちらも細かい機能は違っていても同じような仕組みに感じたと思います。ではこの2つの仕組みからどういう場合に使い分ければいいのか考えてみましょう。
インタフェースと抽象クラスの特徴から使い分けを考える
これまでの学習で両者とも、メソッドの実装を「インタフェースを実装したクラス」や、「抽象クラスを継承したクラス」に強制的に行わせる機能をもっているため似たような仕組みに見えます。
インタフェースと抽象クラスの機能を再確認してみると、インタフェースはimplements修飾子によりクラスに実装されます。その際クラスはインタフェースで宣言されたメソッド(抽象メソッド)は全て実装し、フィールド変数(定数)しか継承することできません。インタフェースはクラスでもないためオブジェクト化は出来ません。
抽象クラスを継承したサブクラスは、抽象クラス内でabstract宣言されたメソッド(抽象メソッド)を全て実装しなければなりません。また抽象クラス内で実装されている機能(フィールド変数、メソッド)も継承することができます。抽象クラスはクラスですが、インタフェース同様にオブジェクト化出来ませんが、サブクラスに機能を継承させることができます。
こうして見ると抽象クラスは、サブクラスにメソッドの実装を強制できる上に、共通処理となる機能を、サブクラスに提供できるので、インタフェースより便利にみえます。
インタフェースと抽象クラスは似た機能を持っていますが、そもそも抽象クラスは根本的に継承の一種であり、インタフェースは必要機能の宣言と捉えて考えると性質が異なってみえないでしょうか。
これまでの内容を踏まえて、抽象クラスとインタフェースの使い分けを簡単に考えると以下になります。
・ 抽象クラスは複数の派生先クラスで、一部の処理の実装が異なる場合に使用する。
・ インタフェースは特定のクラスを共通の方法で取りまとめたい場合に使用する。
両者の仕組みを理解するにはやや難しいですが、似たような機能を持っていても異なる仕組みだと理解して下さい。