トップに戻る

あいさつの例から学ぶ interface と implements

Javaの学習用の教材ページです。

このページで分かること

人もロボットも「あいさつする」ことができます。その共通点を Greeting という型で表し、同じメソッドに渡せるようにします。

implements Greeting は「このクラスは Greeting に書かれたメソッドを使えるようにします」という宣言です。今回の例では、クラス側に public void sayHello() の処理を書きます。

v1 → v2 → v3 の順に、同じ短いプログラムを変更します。各版のコードはすべて掲載しています。青文字と説明で変更箇所を確認できます。

1

v1:人とロボットに、あいさつしてもらう

まずは implements を使わず、2つのクラスを個別に使います。メソッドの名前は、どちらも sayHello です。

保存するフォルダ構成

C:\local-doc\github\pro3\10-a-00v1
  • Human.java
  • Robot.java
  • Main.java

全ファイルを同じフォルダに UTF-8 で保存します。今回は package・import を書きません。

コンパイルと実行

コードの配色
上から1行ずつ実行
cd /d C:\local-doc\github\pro3\10-a-00v1
javac -encoding UTF-8 *.java
java -cp . Main

JDK の javac・java が使える Windows のコマンドプロンプトで実行します。保存先は C:\local-doc\github\pro3\10-a-00v1 です。フォルダがなければ作成してください。最初の cd /d コマンドで、ドライブも含めてこのフォルダに移動します。

Human.java

コードの配色
新規作成
public class Human {
    public void sayHello() {
        System.out.println("こんにちは。");
    }
}

Robot.java

コードの配色
新規作成
public class Robot {
    public void sayHello() {
        System.out.println("コンニチハ。");
    }
}

Main.java

コードの配色
新規作成
public class Main {
    public static void main(String[] args) {
        Human human = new Human();
        Robot robot = new Robot();

        human.sayHello();
        robot.sayHello();
    }
}

表示結果

コードの配色
v1
こんにちは。
コンニチハ。

まず、この4行を読んでみる

Human human = new Human();:人の実体(インスタンス)を作り、human という変数に入れます。左の Human は型、human は変数名です。

Robot robot = new Robot();:同じように、ロボットの実体を作ります。

human.sayHello();:人の sayHello を呼び出します。robot.sayHello();:ロボットの sayHello を呼び出します。

void は「戻り値がない」という意味です。() が空なので、呼び出すときに渡す値もありません。処理は文字を表示する1行だけです。

ここで考えること:同じ名前のメソッドを持っているだけでは、Human と Robot を「あいさつできるもの」という共通の型で扱えません。その型を次で作ります。

2

v2:共通の約束を interface に書く

10-a-00v1 をコピーして 10-a-00v2 を作ります。Greeting.java を追加し、Human と Robot に implements Greeting と @Override を付けます。Main.java は v1 と同じです。

保存するフォルダ構成

C:\local-doc\github\pro3\10-a-00v2
  • Greeting.java
  • Human.java
  • Robot.java
  • Main.java

全ファイルを同じフォルダに UTF-8 で保存します。今回は package・import を書きません。

コンパイルと実行

コードの配色
上から1行ずつ実行
cd /d C:\local-doc\github\pro3\10-a-00v2
javac -encoding UTF-8 *.java
java -cp . Main

JDK の javac・java が使える Windows のコマンドプロンプトで実行します。保存先は C:\local-doc\github\pro3\10-a-00v2 です。フォルダがなければ作成してください。最初の cd /d コマンドで、ドライブも含めてこのフォルダに移動します。

Greeting.java

コードの配色
新規作成
public interface Greeting {
    void sayHello();
}

Human.java

コードの配色
青文字が変更・追加箇所
public class Human implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("こんにちは。");
    }
}

Robot.java

コードの配色
青文字が変更・追加箇所
public class Robot implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("コンニチハ。");
    }
}

Main.java

コードの配色
変更なし
public class Main {
    public static void main(String[] args) {
        Human human = new Human();
        Robot robot = new Robot();

        human.sayHello();
        robot.sayHello();
    }
}

表示結果

コードの配色
v2
こんにちは。
コンニチハ。

interface と implements を1つずつ読む

public interface Greeting:「あいさつできるもの」を表す Greeting というインタフェースを定義します。

void sayHello();:「sayHello という、引数も戻り値もないメソッドを用意する」という約束です。ここには表示の処理を書かず、末尾を ; にします。

Human implements Greeting:Human がこの約束を実装します。Robot も同じ約束を実装します。具体的に何を表示するかは、それぞれのクラスに書きます。

@Override:約束どおりのメソッドを書いているか、コンパイラに確認してもらう目印です。例えば名前を間違えるとコンパイルエラーになります。

インタフェースのこのメソッドは暗黙に public です。Human と Robot の実装にも public が必要です。今回の通常のクラスで sayHello を削除すると、約束を満たさずコンパイルエラーになります。

表示結果は v1 と同じです。追加した宣言によって、Human と Robot の両方を Greeting 型として扱えるようになりました。次でその型を使います。

3

v3:同じメソッドで、人にもロボットにもあいさつしてもらう

10-a-00v2 をコピーして 10-a-00v3 を作ります。変更するのは Main.java だけです。変数の型を Greeting にし、共通の greet メソッドに渡します。

保存するフォルダ構成

C:\local-doc\github\pro3\10-a-00v3
  • Greeting.java
  • Human.java
  • Robot.java
  • Main.java

全ファイルを同じフォルダに UTF-8 で保存します。今回は package・import を書きません。

コンパイルと実行

コードの配色
上から1行ずつ実行
cd /d C:\local-doc\github\pro3\10-a-00v3
javac -encoding UTF-8 *.java
java -cp . Main

JDK の javac・java が使える Windows のコマンドプロンプトで実行します。保存先は C:\local-doc\github\pro3\10-a-00v3 です。フォルダがなければ作成してください。最初の cd /d コマンドで、ドライブも含めてこのフォルダに移動します。

Greeting.java

コードの配色
変更なし
public interface Greeting {
    void sayHello();
}

Human.java

コードの配色
変更なし
public class Human implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("こんにちは。");
    }
}

Robot.java

コードの配色
変更なし
public class Robot implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("コンニチハ。");
    }
}

Main.java

コードの配色
青文字が変更・追加箇所
public class Main {
    public static void main(String[] args) {
        Greeting human = new Human();
        Greeting robot = new Robot();

        greet(human);
        greet(robot);
    }

    public static void greet(Greeting target) {
        target.sayHello();
    }
}

表示結果

コードの配色
v3
こんにちは。
コンニチハ。

左の型と、右で作る実体を分けて読む

Greeting human = new Human(); の左側は「変数を Greeting 型として使う」、右側は「Human の実体を作る」です。Human が implements Greeting を宣言しているので、この代入ができます。

Greeting robot = new Robot(); も同じです。Greeting という型にそろえても、人がロボットに変わるわけではありません。

greet(Greeting target) の target は、呼び出した側から受け取る値を入れる変数(引数)です。Greeting 型なので、Human と Robot のどちらも受け取れます。

static を付けた greet は、この例では static な main から greet(...) と直接呼べます。ここで注目するのは、引数の型が Greeting であることです。

同じ1行でも、実際の相手に応じた処理が動く

呼び出しtarget が参照する実体target.sayHello() の動作
greet(human)Humanこんにちは。
greet(robot)Robotコンニチハ。

greet の中では、相手が人かロボットかを判定する必要がありません。Greeting の約束にある sayHello を呼べます。

4

書き方を整理する

「型を作る」「約束を実装する」「実体を作る」を分けて考えましょう。

書き方この例での役割
interface Greeting共通のメソッドの形を宣言し、Greeting という型を作る。
implements Greetingクラスを Greeting 型として扱えるようにし、約束のメソッドを実装する。
new Human()Human の実体を作る。
Greeting targetあいさつできる相手を受け取る変数。
Human extends ApeHuman が親クラス Ape の機能を継承する。前の教材で学んだ書き方。

よく迷うところ

implements を書くと処理も付いてくる?
今回の Greeting にあるのはメソッドの形だけです。文字を表示する処理は Human と Robot に書きます。

名前が同じなら Greeting 型に入れられる?
この例では implements Greeting の宣言が必要です。v1 の Human を Greeting human = new Human(); と書き換えるだけでは使えません。

new Greeting() と書く?
インタフェース自体のインスタンスは作れません。この例では new Human() や new Robot() で実体を作り、Greeting 型の変数に入れます。

Greeting 型なら、Human のすべてのメソッドが使える?
Human 独自のメソッドを追加しても、Greeting 型の変数からそのまま呼べるわけではありません。この例では、Greeting に宣言した sayHello を共通に呼び出します。

ここでは抽象メソッドを宣言する基本形を学びました。インタフェースに処理を書ける default メソッドなどは、このページの範囲外です。

5

自分で1つ追加してみる

v3 に、あいさつする犬を追加してみましょう。

問題:Dog も同じ greet に渡す

① Dog.java を作り、Greeting を実装してください。sayHello では「ワン!」と表示します。

② Main の main メソッド内で Dog の実体を作り、Greeting 型の変数 dog に入れてください。

③ greet(dog); を追加して、もう一度コンパイル・実行してください。共通の greet メソッドの中身は、そのままで使えます。

解答を見る

Dog.java

コードの配色
全文
public class Dog implements Greeting {
    @Override
    public void sayHello() {
        System.out.println("ワン!");
    }
}

main メソッド内の greet(robot); の後に追加

コードの配色
追加する2行
Greeting dog = new Dog();
greet(dog);

追加後の表示結果

コードの配色
確認
こんにちは。
コンニチハ。
ワン!

元の教材につなげる

元の教材の Lockable は、このページの Greeting にあたります。lock() と unlock() が約束のメソッドです。金庫と鍵付き宝石箱を、同じ Lockable 型として扱います。

元の Lockable[] は、共通の型を使って複数の実体を配列にまとめたものです。このページでは、まず1つずつ引数に渡す方法で共通の型を学びました。

第10回の教材:金庫と宝石箱の例へ 前の教材:package と import