初心者入門レベルの【Javaの勉強】インタフェースによる多重継承の実現
2020/04/02
今回は、Javaで多重継承を可能にする「インタフェース」について勉強してみます。
インタフェースとは
Javaは仕様として、多重継承ができないことを前提としています。
それは多重継承によってプログラムの構造が複雑化するという弊害を避けるためですが、とはいえ多重継承ができたほうがプログラムの構造を逆にシンプルに作ることができる場合もあります。
そのために生み出された仕組みが、Javaで多重継承を可能にする「インタフェース」です。
前回、「ポリモーフィズム」というものを勉強しました。
その「ポリモーフィズム」を実現するために、これまで「継承」「オーバーロード」「オーバーライド」「抽象クラス」といった機能を利用しましたが、今回の「インタフェース」も同様に「ポリモーフィズム」の実現のために利用できる便利な仕組みの一つでもあります。
例えば、“鳥”の「抽象クラス」として“Birds”があったとします。
この“鳥”は、“鳴く”という機能を持つ「抽象メソッド」の“makeSound”と“飛ぶ”という機能を持つ「抽象メソッド」の“fly”を持っています。
そして“鳥”を「スーパークラス」として、“鴨”と“スズメ”という「サブクラス」の“Duck”と“Sparrow”を作って、それぞれ“鳴く”と“飛ぶ”の「抽象メソッド」を「オーバーライド」したとします。
これをテストプログラムにしてみると、以下のようになります。
abstract class Birds {
abstract void makeSound();
abstract void fly();
}
// ガチョウ
class Duck extends Birds {
void makeSound() {
System.out.println("ガーガー");
}
void fly() {
System.out.println("鴨が飛ぶ");
}
}
// スズメ
class Sparrow extends Birds {
void makeSound() {
System.out.println("チュンチュン");
}
void fly() {
System.out.println("スズメが飛ぶ");
}
}
class TestInterface01 {
public static void main(String[] args) {
showSound(new Duck());
showSound(new Sparrow());
showFly(new Duck());
showFly(new Sparrow());
}
public static void showSound(Birds bird) {
bird.makeSound();
}
public static void showFly(Birds bird) {
bird.fly();
}
}
それではここで、“ニワトリ”の「サブクラス」を追加したいとします。
でも“ニワトリ”は飛べないので“fly”の「抽象メソッド」は含めたくないのですが、このようなときにどうするかというと「インタフェース」を使うことによって解決することができるわけです。
「インタフェース」の実装
「インタフェース」は「メソッド」とその「シグネチャ」を定義しますが、その実体を定義することはできません。
この性質から「抽象クラス」に似ていますが、「インタフェース」は「抽象クラス」とは違って多重継承が可能です。
また、「抽象クラス」を継承した「サブクラス」に、さらに「インタフェース」を実装することもできます。
例えば、以下のような「インタフェース」があったとします。
public interface SampleInterface {
int i = 0;
void sample();
}
ちなみに「インタフェース」の宣言する際は“interface”というキーワードを付けて、アクセス修飾子を付ける場合は“public”のみが可能です。
さらにその中で定義される「メソッド」は、自動的に“public abstract”となり、「変数」も自動的に“public final”となって、他の修飾子は使えないので注意が必要です。
そして、この「インタフェース」を実装する場合は、以下のようになります。
public class SuperClass {
}
public class SubClass extends SuperClass implements SampleInterface {
public void sample() {
}
}
このように、「インタフェース」の実装には“implements”といキーワードを使います。
また「インタフェース」の「メソッド」は、前述のとおり自動的に“public”になっているので、これを「オーバーライド」する際も“public”となります。
「インタフェース」を複数実装する場合は、それぞれの「インタフェース」をカンマで区切って記述しますが、実装した「インタフェース」の「メソッド」はすべて漏れなく「オーバーライド」する必要があるので注意しましょう。
ちなみに、一つの「インタフェース」を“extends”によって、別の「インタフェース」に「継承」することも可能です。
それでは前述のテストプログラムに話を戻して、“ニワトリ”の「サブクラス」を追加したプログラムを作ってみましょう。
以下のように「インタフェース」を利用することによって、“fly”の「抽象メソッド」を含めずに“ニワトリ”の「サブクラス」を追加することができます。
abstract class Birds {
abstract void makeSound();
}
interface InterfaceFly {
void fly();
}
// ガチョウ
class Duck extends Birds implements InterfaceFly {
void makeSound() {
System.out.println("ガーガー");
}
public void fly() {
System.out.println("鴨が飛ぶ");
}
}
// スズメ
class Sparrow extends Birds implements InterfaceFly {
void makeSound() {
System.out.println("チュンチュン");
}
public void fly() {
System.out.println("スズメが飛ぶ");
}
}
// ニワトリ
class Rooster extends Birds {
void makeSound() {
System.out.println("コケコッコー");
}
}
class TestInterface02 {
public static void main(String[] args) {
showSound(new Duck());
showSound(new Sparrow());
showSound(new Rooster());
showFly(new Duck());
showFly(new Sparrow());
}
public static void showSound(Birds bird) {
bird.makeSound();
}
public static void showFly(InterfaceFly flyer) {
flyer.fly();
}
}
このように、「抽象クラス」と「インタフェース」を使って多重継承が実現できることが確認できました。
「継承」の関係にない「クラス」同士の「ポリモーフィズム」
さらに「インタフェース」は、「継承」とは無関係に「ポリモーフィズム」を実現することができます。
つまり、まったく関連の無い「クラス」同士で、「メソッド」を共有することもできるわけです。
例えば前述のテストプログラムに、次のような“スーパーマン”の「クラス」の“Superman”を追加したとします。
class Superman {
void superFight() {
System.out.println("スーパーマンが闘う");
}
void superFly() {
System.out.println("スーパーマンが飛ぶ");
}
}
この“スーパーマン”においても“ガチョウ”や“スズメ”と同様に、“鳥”の“飛ぶ”という機能である「抽象メソッド」の“fly”を再利用したい場合を考えてみましょう。
“スーパーマン”は“鳥”とは無関係の「クラス」ですが、以下のように「インタフェース」を実装した「サブクラス」の“FlyingSuperman”を作ることで目的を果たすことができます。
abstract class Birds {
abstract void makeSound();
}
interface InterfaceFly {
void fly();
}
// ガチョウ
class Duck extends Birds implements InterfaceFly {
void makeSound() {
System.out.println("ガーガー");
}
public void fly() {
System.out.println("鴨が飛ぶ");
}
}
// スズメ
class Sparrow extends Birds implements InterfaceFly {
void makeSound() {
System.out.println("チュンチュン");
}
public void fly() {
System.out.println("スズメが飛ぶ");
}
}
// ニワトリ
class Rooster extends Birds {
void makeSound() {
System.out.println("コケコッコー");
}
}
// スーパーマン
class Superman {
void superFight() {
System.out.println("スーパーマンが闘う");
}
void superFly() {
System.out.println("スーパーマンが飛ぶ");
}
}
class FlyingSuperman extends Superman implements InterfaceFly {
public void fly() {
System.out.println("スーパーマンが飛ぶ");
}
}
class TestInterface03 {
public static void main(String[] args) {
showSound(new Duck());
showSound(new Sparrow());
showSound(new Rooster());
showFly(new Duck());
showFly(new Sparrow());
showFly(new FlyingSuperman());
}
public static void showSound(Birds bird) {
bird.makeSound();
}
public static void showFly(InterfaceFly flyer) {
flyer.fly();
}
}
このようにして「インタフェース」を利用することで、「継承」の関係にない「クラス」同士の「ポリモーフィズム」を実現することもできます。
さらにこのことは、機能だけを定義した「クラス」を作って、他の「クラス」に多重継承で自由にその機能を追加することができるということも表しています。
つまり「インタフェース」というのは、「クラス」から機能を分離して独立させたものという見方もできるわけです。
まとめ
今回は、「継承」「オーバーロード」「オーバーライド」「抽象クラス」に続いて、「ポリモーフィズム」を実現する機能として「インタフェース」について勉強しました。
「インタフェース」は、「機能」のみをもつ部品として便利に使えるので、十分に活用できるように理解しておくべき仕組みです。
さらに、Javaのライブラリにも「インタフェース」は多く利用されているので、ドキュメントをチェックしてみることも勉強になるでしょう。
それでは、今回はここまでです。
《 【Javaの勉強】一覧 》