ITエンジニアのプレイングマネージャー化応援サイト

13.インタフェース

この記事では、弊社の新人エンジニア研修の参考にJava8を解説します。

前回は抽象クラスについて解説しました。

今回はインターフェースについて解説します。

インターフェースが何のためにあるのか?を一言で言えばポリモーフィズムのためです

まずはそのことを念頭においてください。

 

インターフェースも抽象クラス同様インスタンス化できません。

しかし、抽象クラスとはまた違った意味で巧妙な役割が与えられています。

インターフェースを考える際にはJavaでは多重継承が禁じられていたことを思い出す必要があります。

Javaではスーパークラスを継承するサブクラスの数に制限はありませんでした。

しかし、サブクラスから見てスーパークラスは一つだけと決まっていました。

これがJavaでは多重継承が禁じられているということの意味です。

では、継承関係にかかわらずポリモーフィズムを使いたいときにはどうしたらいいのでしょうか?

そうです。

インターフェースを使います。

 

そもそもinterfaceとは接点のことです。

人間とコンピュータとの接点をman – machine interface と言ったりしますね。

 

あるいは、パソコンと周辺装置のインターフェースという表現もあります。

例えば、USBというインターフェースを例にとりましょう。

USBの接続口がある製品は何でも、パソコンでもプリンタでもスキャナーでも相互に接続できます。

なぜなら、お互いに一定のルールを内包して共有しているからです。

インターフェースのイメージ

インターフェースのイメージ

Javaのインターフェースのイメージもこのルールを内包して共有するというところにあります。

同じインターフェースを組み込むことでクラスを共通ルールのもとに扱うことができるようになるのです。

 

1.Comparableインターフェース

例によりJava8の標準APIに使用例の範を求めましょう。

前回も使ったIntegerクラスに再登場いただきましょう。

IDEでIntegerクラスの宣言を見てみます。

「implements Comparable」という文字列が見えます。

この部分がインターフェースを実装している部分です。

インターフェースを実装するクラス宣言にはimplementsと書きます

英語のimplementsには実装という意味があります。

ちょうどアタッチメントなどを付ける感覚です。

 

Comparableの文字列の先頭文字が大文字になっています。

クラス同様インターフェースも先頭は大文字です。

compare「比較」にable「できる」という意味の接尾語がついています。

比較できるためのインターフェースを実装しているわけですね。

比較できるというルールを内包して、共有しているわけです。

 

後ろの<Integer>というのはジェネリックス(Generics:総称型)というものです。

詳しくはあとで章を改めてお話しますが、「比較の対象はIntegerクラスのインスタンスだけですよ」とあらかじめ断っているのです。

 

※ちなみにクラスとインターフェースを名前だけで判別する際に接尾語に注目してableがついていたらインターフェースと考えると良いでしょう。
インターフェースを実装するということは、能力を組み込むということだと考えるとナイスなネーミングですね。
ただし、すべてのインターフェースにableがついているわけではないので誤解のないように。

 

では、次にComparable(標準API)から検索してみます。

するとこのように書かれています。

このインタフェースを実装する各クラスのオブジェクトに全体順序付けを強制します。

「全体順序付けを強制します」とあります。

整数値にせよ、文字列にせよ、インスタンスを順序付けできないと並べ替え等ができませんからこれは大切なインターフェースですね。

 

しかし、equalsメソッドのところでも似たような議論がありましたが、何をもって順序を前、後ろと決めるのかという問題があります。

例えば、この新入社員研修のクラス一つとってみても、順序付けの基準は無限に考えられます。

名前のあいうえお順、身長順、年齢順、成績順、などなど。

そこで、Comparableインターフェースでは、「比較する」という抽象メソッドのみを決めて、実装は個々のクラスに任せるのです。

 

IDEを使ってComparableインターフェースの中身を見ていくと一番下に以下のような記述があります。

※()の中のT oもジェネリックスです。Tはタイプ(型)の頭文字で任意のクラスの型を意味しています。

いつものメソッドについている()の後ろの{}がありません。

そうです。中身のない抽象メソッドなのです。

このとき、抽象クラスの時に学んだabstractキーワードがついていないことに着目してください。

インターフェースでは、メソッドは基本、抽象メソッドになります。

※実は、Java8からdefaultメソッドというものも使えるようになったのですが、本研修では触れません。

 

また、仮にpublicがついていなくてもpublicなメソッドになります。

インターフェースに定める抽象メソッドは暗黙的にpulblicで修飾されるのです。

なぜなら、インターフェースは外部に公開することを目的としているものなので、そもそも公開を制限するのは論理矛盾なのです。

 

Comparableインターフェースを実装したクラスでは比較できるという性質を具体的に記述することが必要です。

では、このcompareToメソッドをオーバーライドしている個所をIntegerクラスから探してみましょう。

compareToメソッドの中でcompareメソッドを呼んでいます。

次はこのcompareメソッドに飛んでください。すぐ下にあります。

compareメソッドの中身の三項演算子はxとyを比較して

yが大きければ-1を返す

同じであれば0を返す

xが大きければ1を返す

ということをしているだけです。

では、そのことを確認するサンプルプログラムを書いてみましょう。

<結果>

1
0
-1

確かにそうなっていますね。

ですので、(実用性はともかく)以下のように最大値を求めるプログラムが書けます。

cという変数名で宣言されている配列の型に注目してください。

Comparable型の配列を宣言しています。

つまり、この配列にはComparableインターフェースを実装しているクラスのインスタンスであればなんでも入れることができます。

インターフェースとそのインターフェースを実装したクラスの関係は“is-a”関係です。

パソコン is a コンピュータ

サブクラス is a スーパークラス

といったような論理的な意味関係は以下のように失われてしまっています。

Integer is a Comparable ??

実装クラス is a インターフェース??

とは言えませんね。

しかし、こうすることで後で説明するようにポリモーフィズムを働かせることが可能になります。

 

2.インターフェースのメソッドをオーバーライドする

Comparableインターフェースを活用したプログラムの実例として、以下のようなStudentの年齢の最大値を求めるコードを作成してみました。

オーバーライドしたcompareToメソッドの実装方法に気をつけて以下のようなコードを読んでみてください。

<結果>

Student{年齢=33, 身長=169}

戻り値は-1,0,1に限定されず、2つの値の大小がプラスマイナスで戻ればよいため、

return this.age – as.age; 

とシンプルに書いていますがこれでOKです。

インターフェースを実装したときのクラス図も載せておきます。

 

インターフェースのクラス図

インターフェースのクラス図

 

親クラスの継承のクラス図では実線でしたが、インタフェースの実装のクラス図は点線になります。

また、インタフェースには、<<interface>>というステレオタイプという表記が付きます。

 

ちなみに、抽象メソッドをオーバーライドする利点として、全てのメソッドの名前が同じになるので、

あえてドキュメントを読まなくてもそのメソッドの挙動が推測できるというものがあります。

 

では、一番身長の高い人を見つけるにはどこをどのように変更すればよいでしょうか?

試してみてください。

 

※ちなみに、equalsメソッドがObjectクラスのメソッドをオーバーライドして使うものだったのに対して、CompareToメソッドはComparableインターフェースを実装して使うというところにこの2つの処理の適用範囲が表れていて興味深いですね。つまり、等しいかどうかというのはすべてのクラスに共通の問題ですが、順序付けに関してはそれを必要としないクラスもあるという。

 

3.インターフェースを使ったポリモーフィズム

インターフェースを使ってポリモーフィズムを実現することができます。

サブクラスは一種のスーパークラスだったので、スーパークラスの変数にサブクラスのインスタンスを参照させることができました。

同じように、インターフェース型の参照変数にインターフェースを実装したクラスのインスタンスを代入することができるのです

目的は継承の時と同様に、ポリモーフィズムです。

変数の仮引数で大は小を兼ねたいときに使います。

 

例えば、人間クラスと猫クラスのインスタンスが混在している配列から年齢の一番高いインスタンスを見つけます。

ただし、直接人間と猫のようにクラスが違うものをcompareToメソッドで比較するとキャストが頻発して複雑で変更に弱いプログラムになってしまいます。

ここでは、人間と猫の共通のクラスAnimalを用意してそこにcompareToメソッドを実装しています。

※getClassメソッドはクラス名を取得するメソッドです。

※繰り返しになりますが、複数のクラスを1つのファイルに書く書き方は実務では推奨しません。

<結果>

class chap13.Human{ 年齢=33, 身長=169}

以下にクラス図を示します。

インターフェース型の参照変数にインターフェースを実装したクラスのインスタンスを代入することができるのイメージ

インターフェース型の参照変数の使いどころ

こうしておけば犬クラスやキリンクラスが追加されても同様に扱うことができます。

 

4.複数インターフェースの実装

複数インターフェースを実装している例を見てみましょう。

Javaのソースコードまたは、Stringクラス(標準API)を見てみます。

すると

となっていますね。

このようにimplementsの後に,(カンマ)で区切って複数のインターフェースを実装することができます

 

Comparableインターフェースは既にみました。

Serializable,  CharSequenceというインターフェースは初出ですね。

Serializable インターフェースはマーカーインターフェースといって、marker:目印としての意味だけの特殊なインターフェースです。

ソースコードを見に行っても中身は空です。

Serializable は中身は空ですが、ObjectOutputStream に利用できる、つまりディスクに保存したりネットワーク経由で送受信できるという特性を持っていることを示しています。

この場合のserial(連続)とは直列化ともいい、 0,1の2進数の形にすることを意味します。

この後、Webアプリケーションを学ぶ方はまた、JavaBeansというテーマで出てきますので心に留めておいてください。

 

次に、CharSequence(標準API)というインターフェースを追いかけてみます。

既知のすべての実装クラス:CharBufferSegmentStringStringBufferStringBuilder

すると文字と文字列のところで少し紹介したStringBuilderクラスがCharSequenceインターフェースを実装しているようです。

どのような意図があるのでしょうか?

StringクラスもStringBuilderクラスもCharSequenceインターフェースを実装しているということは、

どちらのクラスもCharSequenceインターフェース型の変数で扱えるということです。

ですから、メソッドを作成するときに仮引数にCharSequence型を宣言しておけば、

実引数としてはStringのインスタンスもStringBuilderのインスタンスも受け取ることができるのです。

わざわざオーバーロードしなくても良いのです。

サンプルコードを掲載しておきます。

<結果>

Hello World.
Goodby World.

 

<まとめ:隣の人に正しく説明できたらチェックを付けましょう>

□インターフェースの存在理由はポリモーフィズムのためである
 
□インターフェースを実装するクラス宣言にはimplementsと書く
 
□インターフェースでは、メソッドは基本、抽象メソッドになるので実装クラスでオーバーライドする
 
□インターフェース型の参照変数にインターフェースを実装したクラスのインスタンスを代入することができる
 
□implementsの後に,で区切って複数のインターフェースを実装することができる

 

まとめができたら、アウトプットとして演習問題にチャレンジしましょう。

問題13.インターフェース

 

今回はインターフェースについて見てきました。

インターフェースを使うことで、Java8でも実質的には多重継承が可能となります。

※多重実現と表現することがあります。

より、柔軟なクラス体系を作り出すことができるということが理解できたのではないでしょうか?

また、抽象クラスかインターフェースかどちらを選択するかという点で言うと、インターフェースに軍配が上がることはこれまでお読みいただいた皆さんには理解いただけるものと思います。

次回のテーマは例外処理です。

例外処理を使って、途中で落ちてしまわない頑強なプログラムを作っていきましょう。

 

 

JavaSE8の解説に戻る

 

【今回の復習Youtube】

044-インターフェース-インターフェースの定義と実装

045-インターフェース-インターフェースとポリモーフィズム

046-インターフェース-インターフェースの配列

 

PAGETOP
Copyright © Say Consulting Group, Inc. All Rights Reserved.