予約語interfaceを使用して独自のインターフェイスを作成します。1個のフィールドと 1個のメソッドを宣言したインターフェイスの作成方法は、例えば以下のようになります。
public interface Sample { public static final int FIELD = 1; public abstract void method(); }
なお、フィールドについては自動的にpublic static final
が、メソッドについては自動的にpublic abstract
が補完されます。したがって、以下のように記述しても同じです。本ウェブサイトでは省略をせずに記述することとします。
public interface Sample { int FIELD = 1; void method(); }
なお、インターフェイスはクラスではないので「extends Object
」は補完されません。上記のサンプルコードには extendsが使用されていないため、親となるインターフェイスは存在しない、ということになります。以下のような記述はビルドエラーになります。
public interface Sample extends Object { // ビルドエラー public static final int FIELD = 1; public abstract void method(); }
インターフェイス Movableと、それを実装する Carクラスを作成し、その動作検証を行っています。
ソースコードは以下の通り。
P201/Movable.java
/** * 移動可能なことを表すインターフェイスです。 */ public interface Movable { /** * 前進します。 */ public abstract void move(); /** * 後退します。 */ public abstract void back(); }
P201/Car.java
/** * 車を表すクラスです。 */ public class Car implements Movable { /** * 名前。 */ private String name; /** * コンストラクタ。 */ public Car() { this("名無しの車"); } /** * コンストラクタ。 * @param name 名前 */ public Car(String name) { this.name = name; } @Override public void move() { System.out.println(this.name + "は前進しました。"); } @Override public void back() { System.out.println(this.name + "は後退しました。"); } }
P201/P201.java
/** * インターフェイスを確認します。 */ public class P201 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { // Car型インスタンスを作成し、Car型変数に紐づける Car car = new Car("白い車"); car.move(); car.back(); // Car型インスタンスを作成し、Movable型変数に紐づける Movable movable = new Car("赤い車"); movable.move(); movable.back(); } }
実行結果は以下の通り。
白い車は前進しました。 白い車は後退しました。 赤い車は前進しました。 赤い車は後退しました。
イメージとしては以下のような感じです。このイメージの中に本来はメソッドも記述すべきですが、省略しています。
抽象クラスを用いたときのように、インターフェイスを用いて多態性を実現することができます。抽象クラスにはクラスの継承関係がありますが、インターフェイスの場合は継承関係を気にせずに実装できる利点があり、遠く離れたクラス同士で多態性を実現できる時があります。
ここでは、Animalクラスを継承した Birdクラスを継承したカラス(Crow)クラスと、飛行機(Airplane)クラスとの関係について考えていきます。なお、カラスと飛行機は、ともに「飛ぶ」ことが可能だとします。
インターフェイスを利用しない場合。Animalクラス、Birdクラス、Crowクラスは M601プロジェクトのものを利用しています。Airplaneクラスは新たに作成しています。
ソースコードは以下の通り。
P202/Animal.java
/** * 動物を表す抽象クラスです。 */ public abstract class Animal { /** * 名前。 */ protected String name; /** * 鳴く回数。 */ protected int cryCount; /** * 名前を設定します。 * @param name 名前 */ public void setName(String name) { this.name = name; } /** * 鳴く回数を設定します。 * @param cryCount 鳴く回数 */ public void setCryCount(int cryCount) { this.cryCount = cryCount; } /** * 鳴きます。 */ public abstract void cry(); }
P202/Bird.java
/** * 鳥を表す抽象クラスです。 */ public abstract class Bird extends Animal { /** * 飛びます。 */ public void fly() { System.out.println(this.name + "は飛びました。"); } }
P202/Crow.java
/** * 犬を表すクラスです。 */ public class Crow extends Bird { /** * コンストラクタ。 */ public Crow() { this(1); } /** * コンストラクタ。 * @param cryCount 鳴く回数 */ public Crow(int cryCount) { this.setName("名無しのカラス"); this.setCryCount(cryCount); } @Override public void cry() { System.out.print(this.name + "「"); for(int i = 0; i < this.cryCount; i ++) { System.out.print("カー"); } System.out.println("」"); } @Override public String toString() { return "[Crow] name:" + this.name + ", cryCount:" + this.cryCount; } }
P202/Airplane.java
/** * 飛行機を表すクラスです。 */ public class Airplane { /** * 飛びます。 */ public void fly() { System.out.println("飛行機は飛びました。"); } }
P202/P202.java
/** * インターフェイスと多態性を確認します(インターフェイスを使用しない場合)。 */ public class P202 { public static void main(String[] args) { Crow crow1 = new Crow(); Crow crow2 = new Crow(5); Airplane airplane1 = new Airplane(); Airplane airplane2 = new Airplane(); crow1.fly(); crow2.fly(); airplane1.fly(); airplane2.fly(); } }
実行結果は以下の通り。
名無しのカラスは飛びました。 名無しのカラスは飛びました。 飛行機は飛びました。 飛行機は飛びました。
イメージとしては以下のような感じです。このイメージの中に本来はメソッドも記述すべきですが、省略しています。
Animalクラス、Crowクラスは、P202プロジェクトのものをそのまま利用しています。Birdクラス、Airplaneクラスは P202プロジェクトのものを改良しています。Flyableインターフェイスは新たに作成しています。
ソースコードは以下の通り。
P203/Flyable.java
/** * 飛行可能なことを表すインターフェイスです。 */ public interface Flyable { /** * 飛びます。 */ public abstract void fly(); }
P203/Animal.java
/** * 動物を表す抽象クラスです。 */ public abstract class Animal { /** * 名前。 */ protected String name; /** * 鳴く回数。 */ protected int cryCount; /** * 名前を設定します。 * @param name 名前 */ public void setName(String name) { this.name = name; } /** * 鳴く回数を設定します。 * @param cryCount 鳴く回数 */ public void setCryCount(int cryCount) { this.cryCount = cryCount; } /** * 鳴きます。 */ public abstract void cry(); }
P203/Bird.java
/** * 鳥を表す抽象クラスです。 */ public abstract class Bird extends Animal implements Flyable { @Override public void fly() { System.out.println(this.name + "は飛びました。"); } }
P203/Crow.java
/** * 犬を表すクラスです。 */ public class Crow extends Bird { /** * コンストラクタ。 */ public Crow() { this(1); } /** * コンストラクタ。 * @param cryCount 鳴く回数 */ public Crow(int cryCount) { this.setName("名無しのカラス"); this.setCryCount(cryCount); } @Override public void cry() { System.out.print(this.name + "「"); for(int i = 0; i < this.cryCount; i ++) { System.out.print("カー"); } System.out.println("」"); } @Override public String toString() { return "[Crow] name:" + this.name + ", cryCount:" + this.cryCount; } }
P203/Airplane.java
/** * 飛行機を表すクラスです。 */ public class Airplane implements Flyable { @Override public void fly() { System.out.println("飛行機は飛びました。"); } }
P203/P203.java
/** * インターフェイスと多態性を確認します。 */ public class P203 { public static void main(String[] args) { Flyable[] flyables = new Flyable[] { new Crow(), new Crow(5), new Airplane(), new Airplane(), new Crow(3), new Airplane(), }; for(int i = 0; i < flyables.length; i ++) { flyables[i].fly(); } } }
実行結果は以下の通り。
名無しのカラスは飛びました。 名無しのカラスは飛びました。 飛行機は飛びました。 飛行機は飛びました。 名無しのカラスは飛びました。 飛行機は飛びました。
先にで作成した Birdクラスの flyメソッドと、Airplaneクラスの flyメソッドは、たまたまメソッド名が一致しているだけでした。今回、Flyableというインターフェイスに flyという抽象メソッドを宣言し、両クラスが Flyableインターフェイスを実装したことで、意図して一致させているということになります。
その結果として、多態性が実現できています。
イメージとしては以下のような感じです。このイメージの中に本来はメソッドも記述すべきですが、省略しています。
Javaでは、多重継承は認められませんが、インターフェイスは幾つでも同時に実装することができまます。予約語 implementsの後ろに、実装したいインターフェイスをコンマで区切って並べるだけです。
M203プロジェクトの Airplaneクラスを利用して複数のインターフェイスを実装してみることにします。ここでは、Flyableインターフェイスと Sendableインターフェイスを作成し、Airplaneクラスでこれらのインターフェイスを実装しています。
ソースコードは以下の通り。
P204/Flyable.java
/** * 飛行可能なことを表すインターフェイスです。 */ public interface Flyable { /** * 飛びます。 */ public abstract void fly(); }
P204/Sendable.java
/** * メッセージを送信可能であることを表すインターフェイスです。 */ public interface Sendable { /** * メッセージを送信します。 */ public abstract void send(); }
P204/Airplane.java
/** * 飛行機を表すクラスです。 */ public class Airplane implements Flyable, Sendable { @Override public void fly() { System.out.println("飛行機は飛びました。"); } @Override public void send() { System.out.println("飛行機はメッセージを送信しました。"); } }
P204/P204.java
/** * 複数のインターフェイスの実装を確認します。 */ public class P204 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { Airplane airplane = new Airplane(); Flyable flyable = airplane; Sendable sendable = airplane; airplane.fly(); airplane.send(); flyable.fly(); // flyable.send(); // ビルドエラー // sendable.fly(); // ビルドエラー sendable.send(); } }
実行結果は以下の通り。
飛行機は飛びました。 飛行機はメッセージを送信しました。 飛行機は飛びました。 飛行機はメッセージを送信しました。
このプログラムでは Airplane型インスタンスを 1個生成しています。参照型変数の型によって、利用可能なメソッドが限定されることが分かります。
イメージとしては以下のような感じです。このイメージの中に本来はメソッドも記述すべきですが、省略しています。
複数のインターフェイスを継承した、新しいインターフェイスを作成することもできます。
ソースコードは以下の通り。
P205/Sendable.java
/** * メッセージを送信可能であることを表すインターフェイスです。 */ public interface Sendable { /** * メッセージを送信します。 */ public abstract void send(); }
P205/Receivable.java
/** * メッセージが受信可能であることを表すインターフェイスです。 */ public interface Receivable { /** * メッセージを受け取ります。 * @param message メッセージ */ public abstract void receive(String message); }
P205/Communicatable.java
/** * メッセージの送受信が可能であることを表すインターフェイスです。 */ public interface Communicatable extends Sendable, Receivable { }
P205/Flyable.java
/** * 飛行可能なことを表すインターフェイスです。 */ public interface Flyable { /** * 飛びます。 */ public abstract void fly(); }
P205/Airplane.java
/** * 飛行機を表すクラスです。 */ public class Airplane implements Flyable, Communicatable { @Override public void fly() { System.out.println("飛行機は飛びました。"); } @Override public void send() { System.out.println("飛行機はメッセージを送信しました。"); } @Override public void receive(String message) { System.out.println("飛行機はメッセージ「" + message + "」を受信しました。"); } }
P205/P205.java
/** * インターフェイスの継承を確認します。 */ public class P205 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { // Airplane型インスタンスを生成し、参照型変数に紐づけます Airplane airplane = new Airplane(); Flyable flyable = airplane; Communicatable communicatable = airplane; Sendable sendable = airplane; Receivable receivable = airplane; airplane.fly(); airplane.send(); airplane.receive("こんにちは"); flyable.fly(); // flyable.send(); // ビルドエラー // flyable.receive("こんにちは"); // ビルドエラー // communicatable.fly(); // ビルドエラー communicatable.send(); communicatable.receive("こんにちは"); // sendable.fly(); // ビルドエラー sendable.send(); // sendable.receive("こんにちは"); // ビルドエラー // receivable.fly(); // ビルドエラー // receivable.send(); // ビルドエラー receivable.receive("こんにちは"); } }
実行結果は以下の通り。
飛行機は飛びました。 飛行機はメッセージを送信しました。 飛行機はメッセージ「こんにちは」を受信しました。 飛行機は飛びました。 飛行機はメッセージを送信しました。 飛行機はメッセージ「こんにちは」を受信しました。 飛行機はメッセージを送信しました。 飛行機はメッセージ「こんにちは」を受信しました。
インターフェイス Sendableと Receivableを継承する、新しいインターフェイス Communicatableを作成し、使用しています。
このプログラムでは Airplane型インスタンスを 1個生成しています。参照型変数の型によって、利用可能なメソッドが限定されることが分かります。
Javaでは多重継承は認められていないので、クラスを継承(extedns)する場合、スーパークラスをひとつしか指定できません。しかし、インターフェイスの場合は、複数のインターフェイスを指定して継承(extends)することができます。
イメージとしては以下のような感じです。このイメージの中に本来はメソッドも記述すべきですが、省略しています。