インターフェース
「ラーメンの味はラーメン屋が決めるのではない.ラーメンの味は客が決めるものだ」と語る店長がいる.ラーメンを構成するものではないが,ラーメンの味を左右する香辛料を追加する振る舞いを追加したい.ただし,ラーメンの種類によって香辛料が変わるので,香辛料を追加する振る舞いの呼び出し方は共通にしておきたい.
このように,香辛料を使う振る舞いの呼び出し方は決まっているが,その具体的な内容が決まっていない場合には,名前のみが決まっている振る舞いを定義して追加することができる
抽象クラスでは,抽象メソッド以外にも,処理内容を含むメソッドを定義することができるが,抽象メソッドのみを定義するための方法がある.それがインターフェースである.抽象的に定義されてるメソッドの具体的な内容を決定する,すなわち,インターフェースの処理内容を定義することを「実装する」という.
逆に,インターフェースを実装しているクラスでは,必ず抽象メソッドを「実装」しなければならない.抽象メソッドを実装しない場合には,抽象メソッドのまま残しておくこともできるが,そのクラスは「抽象クラス」でなければならない.そのため,インスタンス化できないクラスになる.
インターフェースは,変数も定義できるが,定数の場合のみである.通常,何も修飾子をつけないが,自動的にpublic,static,finalとして定義されている.すなわち,これらの修飾子をつけてもつけなくても,自動的にこれらが設定されるため,定数として使う値を定義するためのみに使えると考えておいた方がよいだろう. 他方,インターフェースで定義するメソッドには,abstractが自動的に定義される.定義時にこの修飾子をつけてもつけなくても,自動的に付加された形で定義される.
インターフェースの基本形
インターフェースの定義は以下のような記述である.「interface」のキーワードでインターフェースを定義する.
interface myInterface { (修飾子) (戻り値型) abstractMethod1(); (修飾子) (戻り値型) abstractMethod2(); }
上記のインターフェースを実装するクラスは,以下のように定義する.implementsキーワードを用いて,定義されたインターフェースを定義することを宣言する.
class myClass implements myInterface { (修飾子) (戻り値型) abstractMethod1() { (実装内容) } (修飾子) (戻り値型) abstractMethod2() { (実装内容) } }
具体的に定義すると,インターフェースの定義は以下のようになる.
interface myInterface { public int abstractMethod1(); private void abstractMethod2(); }
インターフェースを実装するクラスの例は定義は以下のようになる.
class myClass implements myInterface { public int abstractMethod1() { System.out.println("hogehoge"); } private void abstractMethod2() { System.out.println("fugagfuga"); } }
(1)一つのインターフェースを実装する
みそラーメンには,香辛料として,唐辛子を入れるに決まっている.そこで,店主は,香辛料をセットするインターフェースをみそラーメンにて実装することにする.
みそラーメンクラスを定義するときに,ラーメンを継承しているのに加えて,スパイスインターフェースを追加する.
UMLでインターフェースを表すときには,クラスを表す表現に「<
public interface Spice { public void setSpice(); }
このSpiceインターフェースを実装するみそラーメンを作る.
public class MisoRamen extends Ramen implements Spice { MisoRamen() { System.out.println("みそラーメンを作るよ"); } public void setTopping() { topping = "ニンニク"; System.out.println("トッピング:" + topping); } public void setSpice() { System.out.println("香辛料:七味唐辛子"); } }
public class Main { public static void main(String[] args) { System.out.println("===みそラーメン(唐辛子つき!)==="); MisoRamen miso = new MisoRamen(); miso.setKatasa(5); miso.setRyou(1); miso.setSoup(Taste.みそ); miso.setTopping(); miso.checkRamen(); miso.setSpice(); } }
===みそラーメン(唐辛子つき!)=== ラーメンつくるよ みそラーメンを作るよ 固さ:5 量:1 スープ:みそ トッピング:ニンニク へいお待ち!量:1固さ:5スープ:みそトッピング:ニンニク 香辛料:七味唐辛子
(2)複数のインターフェースを実装する
「担々麺では香辛料だけでなく,液体で味を強化することも必要ではないか.」そう,担々麺専門店「タンタン」の店主はそうこだわりを語る(嘘).
香辛料だけでなく,液体調味料も追加できるようにしたい.具体的にどのような液体を追加するのかは,ラーメンの種類毎に決めるが,液体調味料を入れるという振る舞いのみは,常に同じ名称にすることで,共通化することができる.すなわち,同じインターフェースを有するクラスでは,同じメソッドで類似した動作がなされることが保証されることを意味している.
インターフェースでは,抽象メソッドのみを定義するという特徴のほかに,抽象クラスとは大きく異なる特徴を持つ.それは,複数のインターフェースを「実装」することができることである.抽象クラスに限らず,クラスでは,「継承」できるクラスは1クラスに限られている.しかし,複数のインターフェースを「実装」することが可能である.
液体調味料を意味するインターフェースを以下のように定義する.public interface Liquid { public void setLiquid(); }
public class TantanMen extends Ramen implements Spice,Liquid { TantanMen() { System.out.println("担々麺を作るよ"); } public void setLiquid() { System.out.println("液体:ラー油"); } public void setSpice() { System.out.println("香辛料:唐辛子"); } public void setTopping() { this.topping = "ひき肉"; System.out.println("トッピング:ひき肉"); } }
複数のインターフェースを持つ担々麺をインスタンス化して,香辛料と液体調味料を入れる様子を確認する.以下のコードにて確認する.
public class Main { public static void main(String[] args) { //担々麺 System.out.println("===担々麺==="); TantanMen tantan = new TantanMen(); tantan.setKatasa(5); tantan.setRyou(2); tantan.setSoup(Taste.みそ); tantan.setTopping(); tantan.setSpice(); tantan.setLiquid(); tantan.checkRamen(); } }
出力は以下のようになる.
===担々麺=== ラーメンつくるよ 担々麺を作るよ 固さ:5 量:2 スープ:みそ トッピング:ひき肉 香辛料:唐辛子 液体:ラー油 へいお待ち!量:2固さ:5スープ:みそトッピング:ひき肉
なお,interfaceがほかのinterfaceを継承することもできる.
演習
とんこつラーメンを作成せよ. 味には「とんこつ」を追加する.トッピングは紅ショウガとし,香辛料はコショウ,液体調味料はお酢とする.