JAVA

最終更新日: 2022.08.02 (公開: 2022.08.02)

Javaのインターフェースはオブジェクト指向に必須!使い方やメリットを解説

Javaのインターフェースはオブジェクト指向に必須!使い方やメリットを解説

 Javaの「インターフェース」は、メソッドとメンバ定数の定義のみを行うための機能です。メソッドの具体的な処理を記述せず、引数と戻り値の型のみを定義することが特徴です。いわば「クラスの設計書」ともいえる存在。Javaの「インターフェース」は、あらゆる場面で活用されているため、必須知識といっても過言ではありません。

Javaでインターフェースを活用すると、「ポリフォーリズム」や「多重継承」といったオブジェクト指向特有の概念を実現可能になります。さらに、クラスメソッドの実装漏れを防げるなど、さまざまな嬉しいメリットもあるのです。本記事では、Javaのインターフェースの概要や実践的な使い方や、メリットなどについてサンプルプログラム付きで解説します。

Javaの「インターフェース(Interface)」とは?

Javaの「インターフェース(Interface)」とは?

Javaの「インターフェース(Interface)」とは、クラスが保有するメソッドをあらかじめ定義できる機能です。いわばクラスの「設計図」のようなもので、各メソッドの引数や戻り値を定義できます。通常のクラスとは異なり、メソッドの具体的な処理内容は記載しないことが特徴です。Javaのインターフェースは、下記のような形式で記載します。

interface Test {

  // メソッドを定義する(実装はしない)

  void test();

}

Javaのインターフェースに記述できるのは、メソッドの定義と定数のみです。メソッドを実装したりフィールド(メンバ変数)を定義したりすると、エラーが出て実行できないので注意が必要です。なお、通常のクラスには「public」「protected」「private」などのアクセス修飾子を付けますが、インターフェースのメソッドにはprivateのみ使用できます。

Javaのインターフェースの特徴

Javaのインターフェースの特徴

Javaのインターフェースには、下記2つの大きな特徴があります。これらの特徴が「Javaのインターフェースを形作っている」といっても過言ではないので、あらかじめ確認しておきましょう。

  • インターフェースはインスタンス化できない
  • インターフェースはクラスにメソッドの実装を強制する

インターフェースはインスタンス化できない

Javaのインターフェースは「インスタンス化」できません。インスタンス化とは、オブジェクトの実体を生成することで、基本的には「new演算子」を使って行います。たとえば、配列をインスタンス化するときは下記のように書きますよね。

int[] array = new int[10];

インスタンス化とは、いわば変数を生成して使えるようにすること。オブジェクトをインスタンス化することで、初めて使えるようになるのです。しかし、インターフェースを下記のようにインスタンス化しようとすると、コンパイルエラーが出て実行できません。

interface Math {

  // メソッドを定義する(実装はしない)

  void math();

}

public class Test {

  

  public static void main(String[] args) {

    // 「Math」はインターフェースなのでインスタンス化できない!

    Math math = new Math();

  }

}

なぜなら、インターフェースは定義だけ行い、中身の実装は行われないからです。設計図だけ用意されていても、その材料がなければ何も作れないですよね。Javaのインターフェースも同じように設計図だけなので、実体化・インスタンス化ができないのです。

インターフェースはクラスにメソッドの実装を強制する

「Javaのインターフェースはどのように使うのか」というと、クラスとして「実装」することで使えるようになります。ただし、インターフェースで定義しておいたメソッドは、必ずクラス側で実装しないとコンパイルエラーになります。つまり、インターフェースから派生したクラスでは、メソッドの実装を強制されるということです。

たとえば、Animalというインターフェースを作成し、それをHumanクラスに継承したとします。Animalに「eat」というメソッドがあった場合、Humanクラスでは「eat」を必ず実装しないといけません。これを実装しない場合は、エラーが出てプログラムを実行できません。インターフェイスの作成や実装の方法はのちほど詳しく解説します。

Javaのインターフェースの使い方

Javaのインターフェースの使い方

Javaのインターフェースの使い方について、「基本的な書き方」と「実践的な使い方」の2つに分けて解説します。

インターフェースの基本的な書き方

Javaのインターフェースは下記のように宣言します。

interface インターフェース名 { 

  定数の定義;

  メソッドの定義;

}

基本的な書き方はクラスと似ていますが、「class」ではなく「interface」と記載することが特徴です。また、前述したようにインターフェース内部で記載できるのは、定数とメソッドの定義だけなので注意してください。

インターフェースを実際に使用するためには、それをクラスという形で実装する必要があります。インターフェースの実装には「implements」を使用し、下記のように記載します。

class クラス名 implements インターフェース名 { 

  インターフェースで定義したメソッドの実装;

  その他の変数やメソッドの実装;

}

少しわかりづらいですが、通常のクラスは「class クラス名」と書くのに対し、インターフェースを実装するクラスでは、そのあとに「implements インターフェース名」と書くことがポイントです。たとえば、「Animal」というインターフェースを「Human」クラスとして実装する場合は、「class Human implements Animal」と記載します。

インターフェースの実践的な使い方

それでは実際に、Javaのインターフェースの使い方を解説していきます。前述したように、インターフェースは「Interface インターフェース名」と記載し、実際の定義は「class クラス名 implements インターフェース名」と書きます。以上のことを踏まえて、さっそくサンプルコードを見ていきましょう。

//サンプルプログラム
// 数学関連の「Math」インターフェースを定義する

interface Math {

  // 定数を定義する

  int LEFT  = 10;

  int RIGHT = 5;

  // 計算メソッドを定義する

  void Calculate();

}

 

// 足し算するためのクラスを実装する

class Add implements Math {

  public void Calculate() {

    System.out.println(LEFT + RIGHT);

  }

}

// 引き算するためのクラスを実装する

class Sub implements Math {

  public void Calculate() {

    System.out.println(LEFT – RIGHT);

  }

}

// 掛け算するためのクラスを実装する

class Mul implements Math {

  public void Calculate() {

    System.out.println(LEFT * RIGHT);

  }

}

// 割り算するためのクラスを実装する

class Div implements Math {

  public void Calculate() {

    System.out.println(LEFT / RIGHT);

  }

}

 

// Mainメソッド

public class Test {

  public static void main(String[] args) {

    // 4つのクラスをインスタンス化する

    Add add = new Add();

    Sub sub = new Sub();

    Mul mul = new Mul();

    Div div = new Div();

    // 各クラスのメソッドを実行する

    add.Calculate();

    sub.Calculate();

    mul.Calculate();

    div.Calculate();

  }

}

 

//実行結果
15

5

50

2

上記のサンプルプログラムでは、「Math」という計算用のインターフェースを定義し、その中には2つの定数と「Calculate」メソッドがあります。mainメソッド内では、四則演算を行うための4つのクラスを実装しています。

クラスを実装するときは、Calculateメソッドを実装しないとコンパイルエラーが出るので要注意。試しに「Add」や「Sub」クラスのCalculateメソッドをコメントアウトすると、プログラムが実行できないはずです。インターフェース特有のことなので難しく感じるかもしれませんが、サンプルコードで少しずつ慣れていきましょう。

Javaのインターフェースを使うメリット5選

Javaのインターフェースを使うメリット5選

Javaのインターフェースを使うことで、下記5つのメリットが得られます。

  • ポリモーフィズムを実現できる
  • 複数のインターフェースを多重継承できる
  • 重要メソッドの実装漏れを防げる
  • 具体的な実装を後回しにできる
  • プログラムの開発効率が上がる

インターフェースのメリット①:ポリモーフィズムを実現できる

Javaのインターフェースを活用すると、オブジェクト指向プログラミングに欠かせない「ポリモーフィズム」を容易に実現できます。ポリモーフィズムとは「多様性」を意味し、複数のオブジェクトが「同じメソッドでそれぞれ異なる結果」を出すことを意味します。

たとえば、動物の鳴き声をイメージするとわかりやすいでしょう。「動物」には「鳴く」という性質がありますが、同じ鳴く声にもその音には大きな違いがあります。その動物の種類に応じた「鳴く」という動作は、ポリフォーリズムの概念を活用すればプログラムで実現できます。

猫は「にゃ~」、犬は「ワンワン」、ひよこは「ぴよぴよ」など、鳴き声には明確な違いがあるはずです。これをJavaのプログラムで、インターフェースを使って記述すると下記のようになります。

//サンプルプログラム
// 動物を表現する「Animal」インターフェースを定義する

interface Animal {

  // 動物が鳴くメソッドを定義する

  void sound();

}

 

// 猫クラスを実装する

class Cat implements Animal {

  public void sound() {

    System.out.println(“にゃ~!”);

  }

}

// 犬クラスを実装する

class Dog implements Animal {

  public void sound() {

    System.out.println(“ワンワン!”);

  }

}

// ひよこクラスを実装する

class Chick implements Animal {

  public void sound() {

    System.out.println(“ぴよぴよ!”);

  }

}

// Mainメソッド

public class Test {

  public static void main(String[] args) {

    // 3つの動物クラスをそれぞれインスタンス化する

    Cat cat = new Cat();

    Dog dog = new Dog();

    Chick chick = new Chick();

    // 各クラスの鳴き声メソッドを実行する

    cat.sound();

    dog.sound();

    chick.sound();

  }

}

 

//実行結果
にゃ~!

ワンワン!

ぴよぴよ!

インターフェースを活用することで、動物の多様性をプログラムで実現できました。鳴き声を表現する「sound」メソッドという動作の入り口を定義し、それぞれのクラスで適切な処理を実行するのがインターフェースの役割です。多様性のあるクラスをまとめて扱いたいとき、インターフェースが大きな力を発揮します。

インターフェースのメリット②:複数のインターフェースを多重継承できる

Javaでインターフェースを使う大きなメリットが、複数のインターフェースを「多重継承」できることです。多重継承とは、主に複数のクラスを継承したサブクラスを指しますが、複数のインターフェースからクラスを作ることもできます。

多重継承はオブジェクト指向に重要な概念で、これを活用することでよりコンパクトなクラス設計が可能です。たとえば、ゲームでキャラクターのクラスを設計する場合を考えてみましょう。ひとくちにキャラクターといっても、単なる村人などの「モブキャラ」と、兵士やプレイヤーなどのキャラクターでは必要な機能が異なります。

モブキャラはただその場にいるだけで良いですが、兵士は武器や戦闘の機能、プレイヤーはさらに操作やレベルアップなど必要な機能が増えます。インターフェースで機能を小分けにし組み合わせることで、不要な機能がない効率的なクラス設計ができるのです。

//サンプルプログラム
// キャラクターを形作る「Character」インターフェースを定義する

interface Character{

  // ダメージ処理を行うメソッドを定義する

  void damage();

}

// キャラクターのAIを実現する「Intelligence」インターフェースを定義する

interface Intelligence{

  // 考えるメソッドを定義する

  void think();

}

// 武器に関する「Weapon」インターフェースを定義する

interface Weapon{

  // 武器で攻撃する

  void attack();

}

// 入力に応じた処理を行う「Control」インターフェースを定義する

interface Control{

  // 移動するメソッドを定義する

  void move();

}

// モブキャラ用クラス(基本要素のみでOK)

class Mob implements Character{

  public void damage() {

    System.out.println(“モブキャラがダメージを受けた!”);

  }

}

// 兵士用クラス(思考や武器の要素も必要)

class Soldier implements Character, Intelligence, Weapon{

  public void damage() {

    System.out.println(“兵士がダメージを受けた!”);

  }

  

  public void think() {

    System.out.println(“兵士の思考…”);

  }

  

  public void attack() {

    System.out.println(“兵士が武器で攻撃した!”);

  }

}

// プレイヤー用クラス(コントロールの要素も必要だがAIは不要)

class Player implements Character, Weapon, Control{

  public void damage() {

    System.out.println(“プレイヤーがダメージを受けた!”);

  }

  

  public void attack() {

    System.out.println(“プレイヤーが武器で攻撃した!”);

  }

  

  public void move() {

    System.out.println(“プレイヤーの移動中…”);

  }

}

// Mainメソッド

public class Test {

  public static void main(String[] args) {

    // 3つのキャラクタークラスをそれぞれインスタンス化する

    Mob mob = new Mob();

    Soldier soldier = new Soldier();

    Player player = new Player();

    // 各クラスのダメージメソッドを実行する

    mob.damage();

    soldier.damage();

    player.damage();

    

    // 兵士とプレイヤーの攻撃メソッドと実行する

    soldier.attack();

    player.attack();

  }

}

 

//実行結果
モブキャラがダメージを受けた!

兵士がダメージを受けた!

プレイヤーがダメージを受けた!

兵士が武器で攻撃した!

プレイヤーが武器で攻撃した!

上記のサンプルプログラムでは、全キャラクター共通の基本的な機能を備えたインターフェースに加えて、AI・武器・操作それぞれのインターフェースを作成しました。モブキャラ・兵士・プレイヤーそれぞれの実装クラスでは、必要な機能を備えたインターフェースを選んでクラスを作り出しています。

ちなみに、インターフェースの多重継承は、継承したいインターフェースを「コンマ(,)」で区切って記載するだけでOK。「class Player implements Character, Weapon, Control」は、Character・Weapon・Controlのインターフェースを使用したクラスだということです。

インターフェースのメリット③:重要メソッドの実装漏れを防げる

Javaのインターフェースを上手に活用すると、重要なメソッドの実装漏れを防ぎやすくなります。前述したように、インターフェースはそれを継承したクラスに、メソッドの実装を強制します。クラス側でいずれかのメソッドが実装されていなければ、コンパイルエラーが出て実行できません。

大規模なプロジェクトになると、設計に変更が生じたときやクラスに機能を追加する場合などに、どうしても実装漏れが生じやすくなります。それが思わぬバグの原因になり、プロジェクトの進行が停滞してしまうことも。インターフェースを使えば、そうしたトラブルを未然に防げます。

インターフェースのメリット④:具体的な実装を後回しにできる

インターフェースを使うことで、具体的な実装を後回しにできるようになります。プログラムの開発現場において、「とりあえず定義だけして、実装は後回しにしたい」という場面はよくあるものです。たとえば、まずは大まかな枠組みだけを決めておいて、細かな機能は実際に検証しながら搭載しておきたいなどです。

また、実装をあとから行う場合は「実装漏れ」の可能性も心配ですが、インターフェースなら問題ありません。あとから機能を追加した場合は、クラス側でも必ず実装しないといけないため、自動的に装漏れを防げるようになるからです。

インターフェースのメリット⑤:プログラムの開発効率が上がる

Javaでインターフェースを活用することにより、プログラムの開発効率が上がります。これは、インターフェースは「オブジェクト指向」を推し進める機能だからです。そもそもオブジェクト指向は、小さなオブジェクトを集めて、プログラムを構成するという概念。

インターフェースはクラスの基礎となる部分を定義し、クラスの作成時に必要なインターフェースを実装します。さらに、インターフェースは多重継承ができるので、不要な機能をクラスに取り込んでしまうことがなく、クラスの設計をコンパクトにできるのです。

結果的に、一度設計したインターフェースは、他のプログラムやプロジェクトに再利用しやすくなります。これにより、開発効率が大幅に高まることは、インターフェースの素晴らしいメリットだといえるでしょう。

Javaのインターフェースの応用知識・テクニック4選

Javaのインターフェースの応用知識・テクニック4選

Javaのインターフェースの基本的な知識を確認したところで、応用テクニックを見ていきましょう。今回は下記4つのテクニックをご紹介します。

  • インターフェースの継承方法
  • インターフェースのdefaultメソッドの使い方
  • インターフェースのstaticメソッド(静的メソッド)の使い方
  • インターフェースとabstractクラス(抽象クラス)の違い

応用①:インターフェースの継承方法

通常のクラスと同じように、Javaのインターフェースは継承もできます。オブジェクト指向プログラミングでは、小さなオブジェクトを組み合わせてプログラムを構成するという点から、継承を活用することが一般的です。

クラスを継承すると、サブクラス(継承先のクラス)ではスーパークラス(継承元のクラス)のすべての機能を含みます。継承方法は通常のクラスと同じく、下記のように「extends」キーワードを使用します。

interface インターフェース名 extends 継承元のインターフェース名

実際に「乗り物」を表現するサンプルプログラムで、インターフェースの継承方法を確認しておきましょう。

//サンプルプログラム
// 乗り物の「Vehicle」インターフェースを定義する

interface Vehicle {

  // アクセル

  void accel();

  

  // ブレーキ

  void brake();

}

// クルマの「Car」インターフェースを、「Vehicle」を継承して定義する

interface Car extends Vehicle {

  // ハンドル

  void handle();

}

// トラックの「Truck」インターフェースを、「Vehicle」を継承して定義する

interface Truck extends Car {

  // 貨物の積み込み

  void load(Object cargo);

}

// 宅配トラック「DeliveryCar」クラスを実装する

class DeliveryCar implements Truck {

  public void accel() {

    System.out.println(“アクセル操作!”);

  }

  public void brake() {

    System.out.println(“ブレーキ操作!”);

  }

  public void handle() {

    System.out.println(“ハンドル操作!”);

  }

  

  public void load(Object cargo) {

    System.out.println(“貨物” + cargo + “を搭載!”);

  }

}

// Mainメソッド

public class Test {

  public static void main(String[] args) {

    // 宅配トラックをインスタンス化する

    DeliveryCar car = new DeliveryCar();

    // 各メソッドを実行する

    car.accel();

    car.brake();

    car.handle();

    car.load(“「楽器」”);

  }

}

 

//実行結果
アクセル操作!

ブレーキ操作!

ハンドル操作!

貨物「楽器」を搭載!

上記のサンプルプログラムでは、基底インターフェースにすべての乗り物に共通する機能を搭載した、「Vehicle」を定義しています。最初のインターフェースを「Car」と「Truck」に継承し、それぞれ必要な機能を追加していることがポイントです。「Vehicle」を実装した「DeliveryCar」クラスでは、結果的に4つのメソッドが搭載されることになります。

このように継承を活用すると、それぞれのインターフェースが「何をするためのもの」であり、実装したクラスが「どのような役割を果たすか」が明確になります。いずれもオブジェクト指向プログラミングの根幹であり、最も重要な部分ですよね。インターフェースの継承と多重継承は、非常に便利な機能なのでぜひ活用してみてください。

応用②:インターフェースのdefaultメソッドの使い方

冒頭で解説したように、Javaのインターフェースではメソッドや定数のみ定義可能で、具体的に実装するとコンパイルエラーとなります。しかし、「defalut修飾子」を付けることにより、インターフェースにもメソッドが定義できます。defalutメソッドの書き方は下記のとおりです。

defalut メソッドの戻り値の型 メソッド名(メソッドの引数) {

  メソッドの実装;

}

具体的には、通常どおりメソッドの実装の前に「defalut」を付けるだけです。実際にサンプルプログラムで、defaultメソッドの使い方を確認しておきましょう。

//サンプルプログラム
// あいさつの言葉を表示するための「Greet」インターフェース

interface Greet {

  default void greet() {

    System.out.println(“こんにちは!”);

  }

}

// 日本語であいさつするクラスを実装する

class Japan implements Greet {

  // オーバーライドしないため実装なし

}

public class Test {

  public static void main(String[] args) {

    // 日本語であいさつするクラスを実装する

    Japan JPN = new Japan();

    

    // 「greet」メソッドを実行する

    JPN.greet();

  }

}

 

//実行結果
こんにちは!

上記のサンプルプログラムでは、最初に「Greet」インターフェース内部の「greet」メソッドで、日本語であいさつする処理を実装しています。「defalut修飾子」を付加していることにより、メソッドを実装していてもエラーになりません。

Greetインターフェースを継承した「Japan」クラスの内部では、何も実装していません。通常はインターフェースのメソッドを実装する必要がありますが、今回はすでに「defaultメソッド」として実装しているため、オーバーライド(上書き)する必要がなければ書く必要がないのです。

ところで、Javaのインターフェースは多重継承が可能ですが、defaultメソッドの処理内容が異なる複数のインターフェースを継承すると、どのようになるのでしょうか。実際に、下記のサンプルプログラムで検証してみましょう。

//サンプルプログラム
// あいさつの言葉を表示するための「Greet」インターフェース

interface Greet {

  default void greet() {

    System.out.println(“こんにちは!”);

  }

}

interface GreetChinese extends Greet {

  default void greet() {

    System.out.println(“你好!!”);

  }

}

// defaultメソッドの内容が異なる2つのインターフェースを継承すると…?

class Somewhere implements Greet, GreetChinese {

  // オーバーライドしないため実装なし

}

public class Test {

  public static void main(String[] args) {

    // どこかであいさつするクラスを実装する

    Somewhere some = new Somewhere();

    

    // 「greet」メソッドを実行する

    some.greet();

  }

}

 

//実行結果
你好!!

上記のように、日本語ではなく中国語のあいさつが表示されました。これは、最も新しいインターフェースの処理が優先されるためです。つまり、「Greet」を継承して「GreetChinese」を作成した時点で、greetメソッドの処理内容はGreetChineseに決まるということになります。

応用③:インターフェースのstaticメソッド(静的メソッド)の使い方

Javaのインターフェースには、メソッドを実装するもうひとつの方法があり、それが「staticメソッド」です。インターフェースのstaticメソッドは、通常のクラスのstaticメソッドと同じ機能を果たします。つまり、インスタンスを生成せずに、クラスの外部から呼び出すことができるのです。staticメソッドの書き方は下記のとおりです。

static メソッドの戻り値の型 メソッド名(メソッドの引数) {

  メソッドの実装;

}

具体的には、通常どおりメソッドの実装の前に「static」を付けるだけです。一方で、呼び出すときは下記のように記載します。こちらも通常のクラスの使い方と同じです。

クラス名.メソッド名(引数);

staticメソッドはインスタンスを生成する必要がないので、クラス名を指定すればそのまま呼び出すことができます。実際にサンプルプログラムで、staticメソッドの使い方を確認しておきましょう。

//サンプルプログラム
// あいさつの言葉を表示するための「Greet」インターフェース

interface Greet {

  // ひとつのメソッドだけ定義できる

  default void greet() {

    System.out.println(“こんにちは!”);

  }

  

  static void explain() {

    System.out.println(“このインターフェースは、あいさつを表示するためのものです”);

  }

}

// defaultメソッドの内容が異なる2つのインターフェースを継承すると…?

class Japan implements Greet {

  // オーバーライドしないため実装なし

}

public class Test {

  public static void main(String[] args) {

    // 日本語であいさつするクラスを実装する

    Japan JPN = new Japan();

    

    // 「greet」メソッドを実行する

    JPN.greet();

    

    // staticメソッドを実行する

    Greet.explain();

  }

}

 

//実行結果
こんにちは!

このインターフェースは、あいさつを表示するためのものです

上記のサンプルプログラムでは、「Greet」インターフェースにstaticメソッドとして「explain」を実装しています。このメソッドを呼び出すときに、「Greet.explain();」のようにクラス名を直接指定していることがポイントです。staticメソッドはそのインターフェースに紐づいているので、「Japan.explain();」や「JPN.explain();」では呼び出せません。

応用④:インターフェースとabstractクラス(抽象クラス)の違い

Javaではインターフェースと同じく、ポリフォーリズムを実現する機能として「abstractクラス(抽象クラス)」があります。抽象クラスは、複数のクラスに共通する機能を備えた「スーパークラス」を作成するための機能で、「抽象メソッド」を持つことが特徴です。先ほど作成した乗り物に関するプログラムを、抽象クラスを使ったものに書き換えましょう。

//サンプルプログラム
// 乗り物の「Vehicle」抽象クラスを定義する

abstract class Vehicle {

  // アクセルの抽象メソッド

  abstract public void accel();

  

  // ブレーキの抽象メソッド

  abstract public void brake();

}

// クルマの「Car」抽象クラスを、「Vehicle」を継承して定義する

abstract class Car extends Vehicle {

  // ハンドルの抽象メソッド

  abstract public void handle();

}

// トラックの「Truck」抽象クラスを、「Vehicle」を継承して定義する

abstract class Truck extends Car {

  // 貨物の積み込みの抽象メソッド

  abstract public void load(Object cargo);

}

// 宅配トラック「DeliveryCar」クラスを実装する

class DeliveryCar extends Truck {

  // 「アクセルの抽象メソッド」を実装する

  public void accel() {

    System.out.println(“アクセル操作!”);

  }

  // 「ブレーキの抽象メソッド」を実装する

  public void brake() {

    System.out.println(“ブレーキ操作!”);

  }

  // 「ハンドルの抽象メソッド」を実装する

  public void handle() {

    System.out.println(“ハンドル操作!”);

  }

  

  // 「貨物の積み込みの抽象メソッド」を実装する

  public void load(Object cargo) {

    System.out.println(“貨物” + cargo + “を搭載!”);

  }

}

// Mainメソッド

public class Test {

  public static void main(String[] args) {

    // 宅配トラックをインスタンス化する

    DeliveryCar car = new DeliveryCar();

    // 各メソッドを実行する

    car.accel();

    car.brake();

    car.handle();

    car.load(“「楽器」”);

  }

}

 

//実行結果
アクセル操作!

ブレーキ操作!

ハンドル操作!

貨物「楽器」を搭載!

先ほどのプログラムからの主な変更点は、インターフェース定義時の「interface」を「abstract class」に変更し、抽象メソッドの戻り値の前に「abstract」を付けたことです。書き方を変えれば、抽象クラスもインターフェースと同じように使えることがわかりますね。ただし、Javaのインターフェースと抽象クラスは、下記の点で大きく異なります。

インターフェース 抽象クラス
アクセス修飾子 publicのみ 基本的に制限なし
定義できるメンバ メソッドの実装

defaultメソッド

staticメソッド

クラス定数

基本的に制限なし

(抽象メソッドなしでもOK)

多重継承 可能 不可

上記のように、インターフェースは多重継承できますが、抽象クラスはひとつのクラスからしか派生できません。一方で、抽象クラスはアクセス修飾子や定義できるメンバに制限がなく、この点では通常のクラスに近いといえるでしょう。

なお、「インスタンスを生成できない」という点では、インターフェースも抽象クラスも同じです。抽象クラスは抽象メソッドなしでもOKなのですが、これは「あとから抽象メソッドを追加する」ケースも想定しているためだと考えられます。

一般的には、抽象クラスはサブクラスで使う共通処理を記載する目的、インターフェースは事前のメソッド定義で実装漏れを防ぐために使うことが多いです。しかし、両者の境界線はあいまいな部分も多いので、インターフェースと抽象クラスの使い分けをそれほど意識する必要はないでしょう。

「関数型インターフェース」についても知っておこう!

「関数型インターフェース」についても知っておこう!

Javaのインターフェースに関連する機能が、「関数型インターフェース」と呼ばれるものです。メソッドがひとつしかないインターフェースを、「関数型インターフェース」といいます。関数型インターフェースは、「ラムダ式」と密接なつながりがあることがポイントです。ラムダ式はプログラムをより簡潔に記載するために活用し、下記のように記載します。

(引数) -> {

  処理内容;

}

インターフェースは、メソッドをひとつだけ有するインターフェースを、簡潔なコードでインスタンス化するために使用します。実際に、関数型インターフェースとラムダ式を活用することにより、コードがどれくらい簡潔化するか確認してみましょう。

//サンプルプログラム
// 年齢を表示するインターフェース

interface Age {

  // メソッドがひとつしかない「関数型インターフェース」

  void print();

}

public class Test {

  public static void main(String[] args) {

    // 「匿名クラス」でインターフェースの実装とクラスのインスタンス化を行う

    Age objA = new Age() {

      // printメソッドをオーバーライドする

      @Override

      public void print() {

        // 年齢を表示

        System.out.println(“年齢は20歳です”);

      }

    };

    

    // objAのメソッドを実行する

    objA.print();

    

    // 「ラムダ式」でインターフェースの実装とクラスのインスタンス化を行う

    Age objB = () -> {

      System.out.println(“年齢は20歳です”);

    };

    // objBのメソッドを実行する

    objB.print();

  }

}

 

//実行結果
年齢は20歳です

年齢は20歳です

最初にひとつのメソッド定義のみ有する関数型インターフェースを定義し、mainメソッド内で2つの方法でインターフェースを実装し、インスタンスを生成しています。1つ目の「匿名クラス」を使う方法と比べて、2つ目のラムダ式ではわずか1行で完了します。もうひとつの例を活用して、関数型インターフェースに慣れてみましょう。

//サンプルプログラム
// あいさつの言葉を表示するためのインターフェース

interface Greet {

  // ひとつのメソッドだけ定義できる

  public void greet();

}

public class Test {

  public static void main(String[] args) {

    // 日本語であいさつするクラスを実装する

    Greet JPN = () -> {

      System.out.println(“こんにちは!”);

    };

    // 中国語であいさつするクラスを実装する

    Greet CHN = () -> {

      System.out.println(“你好!”);

    };

    // 英語であいさつするクラスを実装する

    Greet GBR = () -> {

      System.out.println(“Hello!”);

    };

    // ドイツ語であいさつするクラスを実装する

    Greet DEU = () -> {

      System.out.println(“Hallo!”);

    };

    // フランス語であいさつするクラスを実装する

    Greet FRA = () -> {

      System.out.println(“Bonjour!”);

    };

    // ロシア語であいさつするクラスを実装する

    Greet RUS = () -> {

      System.out.println(“Привет!”);

    };

    // トルコ語であいさつするクラスを実装する

    Greet TUR = () -> {

      System.out.println(“Merhaba!”);

    };

    

    // それぞれのクラスのメソッドを実行する

    JPN.greet();

    CHN.greet();

    GBR.greet();

    DEU.greet();

    FRA.greet();

    RUS.greet();

    TUR.greet();

  }

}

 

//実行結果
こんにちは!

你好!

Hello!

Hallo!

Bonjour!

Привет!

Merhaba!

上記のサンプルプログラムでは、ラムダ式を活用して各言語でのあいさつ文を実装しています。もし関数インターフェースを使わなければ、下記のようなクラスを言語ごとに作成しないといけないので、プログラムが非常に冗長になってしまいます。

interface Greet {

  public void greet();

}

class JapaneseGreet implements Greet {

  public void greet() {

    System.out.println(“こんにちは!”);

  }

}

class ChineseGreet implements Greet {

  public void greet() {

    System.out.println(“你好!”);

  }

}

…(以下略)

Javaのラムダ式や関数型インターフェースは、構文が特殊なので取っつきにくい印象があるでしょう。しかし、これらの機能を活用すれば、非常に効率的なプログラミングができるようになるので、ぜひ参考にしてみてください。

Javaのプログラミングに「インターフェース」を活用しよう!

Javaのプログラミングに「インターフェース」を活用しよう!

Javaは「インターフェース」は、オブジェクト指向プログラミングの効率化に役立つ機能です。インターフェースは、クラスで使用するメソッドをあらかじめ定義しておくことが特徴。インターフェースを活用すると、クラス側でメソッドを確実に実装できるので、思わぬミスやバグを防げます。

さらに、各オブジェクトの機能や役割が明確化し、オブジェクトを他のプログラムに流用しやすくなるので、プログラミングの効率が大幅に高まることも魅力。関数型インターフェースやラムダ式など応用テクニックも活用すると、より多様なコードが書けるようになります。この機会に、ぜひJavaのインターフェースを活用してみましょう。

著者紹介

084tjsfdsf983asdlk03

084tjsfdsf983asdlk03

コードカキタイがオススメする記事!

  1. 子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

    2022.01.06

    子供におすすめのプログラミングスクール10選!学習メリットや教室選びのコツも紹介

    #プログラミングスクール

  2. 【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

    2022.01.06

    【完全版】大学生におすすめのプログラミングスクール13選!選ぶコツも詳しく解説

    #プログラミングスクール

  3. 【未経験でも転職可】30代におすすめプログラミングスクール8選!

    2022.01.06

    【未経験でも転職可】30代におすすめプログラミングスクール8選!

    #プログラミングスクール

  4. 初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

    2022.01.06

    初心者必見!独学のJava学習方法とおすすめ本、アプリを詳しく解説

    #JAVA

  5. 忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

    2022.01.06

    忙しい社会人におすすめプログラミングスクール15選!失敗しない選び方も詳しく解説

    #プログラミングスクール

  1. 【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

    2022.01.06

    【無料あり】大阪のおすすめプログラミングスクール14選!スクール選びのコツも紹介

    #プログラミングスクール

  2. 【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

    2022.01.06

    【目的別】東京のおすすめプログラミングスクール20選!スクール選びのコツも徹底解説

    #プログラミングスクール

  3. 【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

    2022.01.06

    【無料あり】福岡のおすすめプログラミングスクール13選!選び方も詳しく解説

    #プログラミングスクール

  4. 【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

    2022.01.06

    【徹底比較】名古屋のおすすめプログラミングスクール13選!選び方も詳しく解説

    #プログラミングスクール

  5. 【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

    2022.01.06

    【徹底比較】おすすめのプログラミングスクール18選!失敗しない選び方も徹底解説

    #プログラミングスクール