4-11では抽象クラスを説明しました。抽象クラスをさらに抽象化したものにインタフェースがあります。今回は、インタフェースの特徴や役割について説明します。
もくじ
インタフェースとは
インタフェースは「フィールドは全て定数」「メソッドは全て抽象メソッド」となる、クラスのようなものです。
しかし、クラスではないのでインタフェースからインスタンス生成を行うことはできません。
インタフェースは実装(継承みたいなもの)されることが大前提で作成します。抽象クラスと同様に、「インタフェースを作成するかどうかはどのような設計を行うか」が重要となります。
抽象クラス同様、定義するメリットやインタフェースの使いどころなどが分かりにくい場合は、まずは文法だけを覚えるのも学習方法の一つです。
インタフェースの特徴
インタフェースには以下の特徴があります。
- 宣言の際は、classではなくinterfaceで宣言する
- 実装はimplementsキーワードを利用する
- インタフェースは複数実装できる
- インタフェースからはインスタンス生成できない
- メソッドはすべて「public」「abstract」が付加され抽象メソッドとなる
- 抽象クラスと違い通常のメソッドは定義できない
- フィールドはすべて「pubic」「static」「final」が付加された定数となる
- インタフェースを実装したクラスは抽象メソッドをオーバーライドする必要がある
- 抽象メソッドをオーバーライド時のアクセス修飾子はpublic指定が必要
インタフェースは以下のようにinterfaceキーワードで定義します。
インタフェース宣言 | interface インタフェース名{ |
---|---|
記述例 | interface Machine{ |
インタフェース内のメソッドは全て抽象メソッドとなります。メソッドは全て自動的に「public」「abstract」が付加されます。
※抽象メソッドの説明は抽象クラスの項を参考にしてください
インタフェース内のメソッド | 戻り値の型 メソッド名(引数) ; |
---|---|
記述例 | void powerSwitch() ; |
インタフェース内のフィールドは全て自動的に「public」「static」「final」が付加されます。定数となる為、名前は大文字で設定しましょう。
インタフェースを利用するクラスでは、インタフェースをextendsを用いて継承するのではなく、implementsで実装を行います。
インタフェースの実装 | class クラス名 implements インタフェース名{ |
---|---|
記述例 | class Curry implements Machine{ |
同一クラスで複数のインタフェースを実装することもできます。その際は「implements Machine, Robot」といったように,(カンマ)で区切ります。
インタフェース サンプルプログラム
以下のプログラム「Machine.java」「TV.java」「Vacuum.java」「Number36.java」という名前でworkフォルダ内に保存します。
保存が完了したら、コマンドプロンプトを起動し、Number36.javaをコンパイルおよび実行をしてみましょう。
※プログラムが思いのほか長くなってしまったため、記述するのに時間がかかりそうな人はコピーペーストで確認してください–;)
1 2 3 4 5 6 |
//インタフェース interface Machine{ //抽象メソッド void powerSwitch(); boolean powerStatus(); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
//Machineインタフェースを実装したクラス class TV implements Machine{ private boolean power; private int channel; private int volume; //コンストラクタ TV(){ power = false; channel = 2; volume = 0; } //メソッドを実装(オーバーライド) public void powerSwitch(){ if(power == false){ System.out.println("テレビの電源を入れました"); System.out.println("チャンネル:" + channel); System.out.println("音量:" + volume); }else{ System.out.println("テレビの電源を切りました"); } power = !power; } //メソッドを実装(オーバーライド) public boolean powerStatus(){ return power; } void setChannel(int ch){ if(ch >= 1 && ch <= 12){ channel = ch; } System.out.println("[変更後]テレビのチャンネル:" + channel); } int getChannel(){ return channel; } void volumeUp(){ if(volume < 10){ volume++; System.out.println("音量を上げました[音量:" + volume + "]"); } } void volumeDown(){ if(volume > 0){ volume--; System.out.println("音量を下げました[音量:" + volume + "]"); } } int getVolume(){ return volume; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
//Machineインタフェースを実装したクラス class Vacuum implements Machine{ private boolean power; private int level; //コンストラクタ Vacuum(){ power = false; level = 1; } //メソッドを実装(オーバーライド) public void powerSwitch(){ if(power == false){ level = 1; System.out.println("掃除機の電源を入れました"); System.out.println("掃除機の吸い込む強さ:" + level); }else{ System.out.println("掃除機の電源を切りました"); } power = !power; } //メソッドを実装(オーバーライド) public boolean powerStatus(){ return power; } void setLevel(int le){ if(le >= 1 && le <= 4){ level = le; } System.out.println("[変更後]掃除機の吸い込む強さ:" + level); } int getLevel(){ return level; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
class Number36{ public static void main(String[] args){ TV tv = new TV(); tv.powerSwitch(); tv.volumeUp(); tv.setChannel(3); System.out.println(); Vacuum vacuum = new Vacuum(); vacuum.powerSwitch(); vacuum.setLevel(4); vacuum.powerSwitch(); } } |
実行例
C:\work>javac Number36.java
C:\work>java Number36
テレビの電源を入れました
チャンネル:2
音量:0
音量を上げました[音量:1]
[変更後]テレビのチャンネル:3
掃除機の電源を入れました
掃除機の吸い込む強さ:1
[変更後]掃除機の吸い込む強さ:4
掃除機の電源を切りました
※実行例はコンパイルおよび実行までの例を表示しています。
サンプルプログラムの解説
サンプルプログラムの関係性は以下のようなイメージとなっています。
インタフェースである[Machine]には抽象メソッドとして2つのメソッド[powerSwitch()][powerStatus()]が定義されています。抽象メソッドなので、処理は記述しません。
このインタフェースを実装するクラスはこの抽象メソッドをオーバーライドする必要があり、その処理内容も各クラスで個別に記述することとなります。
インタフェースの抽象メソッドを実装する2つのクラスが[TV]と[Vacuum]です。
それぞれのクラスで抽象メソッドの処理を実装(記述)する他に、各々で独自のメソッド※も持っているクラスとなります。
※TVだとsetChannel()など、VacuumだとgetLevel()など
以下がインタフェース及びクラスの説明をした表となります。(実行はクラスは含まれていません)
インタフェース名 | |||||
---|---|---|---|---|---|
Machine | |||||
アクセス 修飾子 |
修飾子 | メソッド名 | 戻り値の型 | 引数 | 処理内容 |
public | abstract | powerSwitch | void | なし | 電源スイッチに関するメソッド |
public | abstract | powerStatus | void | なし | 電源の状態を返すメソッド |
※publicとabstractは自動的に付与されている(プログラム内での記述はなし)
クラス名 | ||||
---|---|---|---|---|
TV | ||||
アクセス修飾子 | フィールド名 | 型 | 初期値 | |
private | power | boolean | なし(宣言のみ) | |
private | channel | int | なし(宣言のみ) | |
private | volume | int | なし(宣言のみ) | |
アクセス修飾子 | コンストラクタ名 | 戻り値の型 | 引数 | 処理内容 |
なし | TV | void | なし | 各種フィールドを初期化する |
アクセス修飾子 | メソッド名 | 戻り値の型 | 引数 | 処理内容 |
public | powerSwitch | void | なし | 電源のオンオフを切り替える |
public | powerStatus | boolean | なし | 電源の状態を返す |
なし | setChannel | void | int | チャンネルを設定する (設定できるチャンネルは1から12) |
なし | getChannel | int | なし | チャンネルの値を返す |
なし | volumeUp | void | なし | 音量を上げる(上限値は10) |
なし | volumeDown | void | なし | 音量を下げる(下限値は0) |
なし | getVolume | int | なし | 音量の値を返す |
※色がついている行はオーバーライド(実装)を行ったメソッド
※オーバーライドしたメソッドはpublicである必要があります
クラス名 | ||||
---|---|---|---|---|
Vacuum | ||||
アクセス修飾子 | フィールド名 | 型 | 初期値 | |
private | power | boolean | なし(宣言のみ) | |
private | level | int | なし(宣言のみ) | |
アクセス修飾子 | コンストラクタ名 | 戻り値の型 | 引数 | 処理内容 |
なし | Vacuum | void | なし | 各種フィールドを初期化する |
アクセス修飾子 | メソッド名 | 戻り値の型 | 引数 | 処理内容 |
public | powerSwitch | void | なし | 電源のオンオフを切り替える 電源オンの際は強さは1で設定 |
public | powerStatus | boolean | なし | 電源の状態を返す |
なし | setLevel | void | int | 掃除機の強さ[レベル]を設定する (設定できる強さは1から4) |
なし | getLevel | int | なし | 強さの値を返す |
※色がついている行はオーバーライド(実装)を行ったメソッド
※オーバーライドしたメソッドはpublicである必要があります
補足 インタフェースのメリット
インタフェースや抽象クラスは、一見すると「記述量が多くなる」「ややこしい」などのデメリットがあり、かつ作成しなくてもプログラムを記述することはできます。また、設計を間違ってしまうと逆にコード内容が複雑化および長くなる可能性もあります。
しかし、プログラムを設計するうえでインタフェースを活用することで様々なメリットが生まれます。
インタフェースの様々なメリット
今回は、平易な説明にとどめますので、詳細な説明が必要な人はほかのサイト等などを閲覧してください。
インタフェースを定義する視点
これから作成するプログラムでの必要なメソッドのシグネチャのみを指示し、処理は実装クラスで記述させる。
これは抽象メソッドをオーバーライドさせることによるメリットとなります
インタフェースを実装する視点
シグネチャは決まっているため、処理内容のみに注視して記述ができる。
これは抽象メソッドをオーバーライドすることによるメリットとなります
インタフェースを実装したクラスを利用する側の視点
どのインタフェースが実装されているかで、どのような機能(メソッド)を利用できるかが一見してわかる。また、シグネチャが一緒なので、処理内容に違いがあっても気にせず(意識せずに)、メソッドを利用できる。
これは抽象メソッドがオーバーライドされていることによるメリットとなります
上記で、一番簡単なのはインタフェース定義に見えます。しかし、定義方法を間違うと、実装側と実装クラス利用側どちらにも悪影響が出てきますので、インタフェース定義はかなり重要な役割を担います。
インタフェース 復習問題
- インタフェースの特徴として間違っているものを選んでください。
- 解答群
- インタフェースを宣言する際はclassの左にinterfaceを記述する
- インタフェースは抽象メソッドしか定義できない
- インタフェースで定義されたメソッドは全てpublicとなる
- インタフェースで定義されたフィールドは全て定数となる
- インタフェースの特徴として間違っているものを選んでください。
- 解答群
- インタフェースを実装する際はextendsキーワードを記述する
- インタフェースを実装する際はimplementsキーワードを記述する
- メソッドをオーバーライドする際はpublicでなければならない
- インタフェースは複数実装が可能である
- インタフェースの特徴として間違っているものを選んでください。
- 解答群
- インタフェースで定義されたフィールドは自動的にpublicとなる
- インタフェースで定義されたフィールドは自動的にstaticとなる
- インタフェースで定義されたフィールドは自動的にfinalとなる
- インタフェースで定義されたフィールドは自動的にabstractとなる
- お疲れ様でした。
まとめ
- インタフェース宣言の際は、classではなくinterfaceで宣言する
- インタフェースは継承ではなく実装させる
- 実装はimplementsキーワードを利用する
- インタフェースは複数実装できる
- インタフェースからはインスタンス生成できない
- メソッドはすべて「public」「abstract」が付加され抽象メソッドとなる
- 抽象クラスと違い通常のメソッドは定義できない
- フィールドはすべて「pubic」「static」「final」が付加された定数となる
- インタフェースを実装したクラスは抽象メソッドをオーバーライドする必要がある
- 抽象メソッドをオーバーライド時はpublic指定が必要