サンプルコード
本記事ではゲームプログラムっぽいサンプルコードとともに、Interfaceそのもの・呼ばれる側・呼ぶ側の解説をする。Interfaceって何?という方向けの記事です。
Interfaceファイル
メソッド(ふるまい)の定義を記載したファイル。
クラスではない。
/** プレイヤーがアクセス可能である。*/
public interface アクセス可能なもの{
/** アクセスされたときのふるまい。*/
public void アクセスする();
}
上記の通り、メソッドの中身も空っぽでよく、
メソッドの定義(平たく言うと、呼び出し方)さえ書かれていればOK、というのがInterface。
乱暴に言うと、中身空っぽの定義ファイルである。
Interfaceとして呼び出される側(Interfaceの実装クラス)
Interfaceは他のクラスに実装(Implement)されることで、使えるようになる。
public class 宝箱 implement アクセス可能なもの{
public void アクセスする(){
//中身を獲得する処理。
}
}
public class 商人 implement アクセス可能なもの{
public void アクセスする(){
//取引画面を開く処理。
}
メソッドの名前や引数などはInterfaceに従う必要がある。
が、メソッドの中身の処理は実装クラス側が好きに決められる。
なので、処理は同じでも良いし、全く違っても良い。自由。
上記例の「商人」と「宝箱」というのは、客観的に見て、関連が薄い機能ではあるが、
「プレイヤーがアクセス可能」という1点だけに注目すると、共通であるともいえる。
乱暴な言い方にはなるが、
Interface記載のふるまいを実装しよう!と思ったクラスには、Interfaceは実装してよい。
(ここでは「プレイヤーがアクセス可能である」というのが「ふるまい」)
注意点だが、Interfaceを実装(Implements)する場合、Interfaceのメソッドは
漏れなく使えるようにしておかないといけない。
上記の例では「アクセスする」メソッドをクラスにて使える状態にしておく必要がある。
そうでないと、コンパイルエラーになってしまう。
Interfaceを呼び出す側
呼び出す処理のほうも書いてみる。
以下を見るとわかるが、呼び出す側は、Interfaceと階層的に関係する必要はなく、Interface名と呼び出すメソッドを知っているだけで良い。
public void 〇ボタンを押す(){
//近隣のアクセス可能な対象から最も近いものを取得する機能(詳細略)
アクセス可能なもの accessable=getAccessable();
//取得した対象にアクセスする。
accessable.アクセスする();
}
}
ここで注目してほしいのだが、
呼び出す側のコードには、「宝箱」「商人」といった要素が一切出てきていない。
「アクセス可能なもの」に対して「アクセスする」という抽象的な表現になっている。
抽象的に書ける、ということは、実は非常にありがたい。なぜなら、
・宝箱に対する呼び出しコード
・商人に対する呼び出しコード
をそれぞれに書く必要がないからである。
もっと言うと、「ちょっとレアな宝箱」だの「秘密のボタン」だの、
アクセス可能な対象を増やされても安心だといえる。
<<以降、抽象的にあつかえるというのは原則良い、という立場で記載します>>
Interfaceの使われどころ
Interfaceが使われる場合、下記のうちの1つ、もしくは組み合わせが目的であることが多い。
自由な継承
クラスの継承元、すなわち「親」は1つのみ、というのがJavaの大原則、ルールである。
public class Child extends Parent1,Parent2{
//コンパイルエラーになる。
}
が、Interfaceはいくつでも実装元として使える。
public class Child Implements Interface1,Interface2{
//問題なし。
}
抽象的なプログラミングを行う際、
Interface1という抽象名でも扱いたいが、Interface2という抽象名でも扱いたい・・・
というケースはよくある。
Interfaceは多重継承可能であるので特に気にせず、両方のInterfaceを実装できる。
ソースに対するマーカー
Interfaceは中身が空っぽ、かつクラスにいくつも実装できる、という特徴は、
クラスに何かしらのマーキングをしたいというニーズにぴったりである。
この思想で使われているのがアノテーション(@xxx)という機能である。
アノテーションは、@InterfaceというInterfaceの一機能である。
自動生成の殻として
interfaceのメソッドは中身が空であり、
かつ、実装クラスが別で生成されるという特徴は、
自動生成ライブラリの思想とよく適合する。
(作りたい内容だけ定義し、中身はライブラリが自動生成したものを作成する)
domaやlombokなど。
Interfaceでも中身を書きたいという場合
ガワもいいけど、やはり処理を共通化したい、というケースは往々にして発生する。
それに対するアンサーが、defaultメソッドと、(Interfaceの)staticメソッドである。
defaultメソッド
defaultメソッドという仕組みがあり、中身の処理を記載できる。
/** プレイヤーがアクセス可能である。*/
public interface Example{
/** 共通的な処理。*/
public default void 共通的な処理(){
//共通的な処理。
}
}
staticメソッド
こちらも同様に、中身の処理を記載できる。
/** プレイヤーがアクセス可能である。*/
public interface Example2{
/** 共通的な処理。*/
public static void 共通的な処理(){
//共通的な処理。
}
}
defaultとの使い分けは、基本的には
staticというもののふるまい(インスタンス化されない、継承できない、など)に注意していただければよい。
余談
- Interfaceには、定数も定義できる。(public static finalな変数)
- Interfaceのクラス・メソッドは原則、publicであるとして扱われる。
- Java以外では、Interfaceという名前の機能はあまり見たことが無い。抽象クラスを使うことも多い気がする。動的型付け言語だと、ダックタイピングという思想が引き合いに出されることが多い。
- 後半ちょっと力尽きてます。
参考文献
徹底攻略 Java SE11 Silver問題集(インプレス)