継承関係を持つクラスで流れるようなインターフェイス

(AA記法の問題については別エントリにまとめました。)

流れるようなインターフェイス(fluent interface)とは

setterで戻り値をvoidじゃなくてthisを戻すようにするとメソッドチェーン出来て便利ですねという話。

Martin Fowler's Bliki in Japanese - 流れるようなインターフェース Martin Fowler's Bliki in Japanese - 流れるようなインターフェース


例:

public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public Person setName(String name) {
        this.name = name;
        return this;
    }
    public int getAge() {
        return age;
    }
    public Person setAge(int age) {
        this.age = age;
        return this;
    }
}

まあ、継承を考えなければ、上のようにsetterでthisを戻せば良い。

呼び出す時はこんな感じ。

    Person person = new Person();
    person.setAge(17).setName("H.P.Lovecraft");

流れるようなインターフェイスって何てスッキリして便利なんだろう!感激・・・と思っていた時期もありました。継承を意識するまでは。

継承関係を持つクラスで流れるようなインターフェイス

では、流れるようなインターフェイスでPersonのサブクラスEmployeeを定義してみましょう

public class Employee extends Person {

    private int empId;
    private String division;
    
    public int getEmpId() {
        return empId;
    }
    public Employee setEmpId(int empId) {
        this.empId = empId;
        return this;
    }
    public String getDivision() {
        return division;
    }
    public Employee setDivision(String division) {
        this.division = division;
        return this;
    }
}
          ____
       / \  /\  キリッ
.     / (ー)  (ー)\
    /   ⌒(__人__)⌒ \
    |      |r┬-|    |
     \     `ー'´   /
    ノ            \
  /´               ヽ
 |    l              \
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、.
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))


          ____
        /_ノ  ヽ、_\
 ミ ミ ミ  o゚((●)) ((●))゚o      ミ ミ ミ   どこが難しいんだおwwww
/⌒)⌒)⌒. ::::::⌒(__人__)⌒:::\   /⌒)⌒)⌒)
| / / /     |r┬-|    | (⌒)/ / / //
| :::::::::::(⌒)    | |  |   /  ゝ  :::::::::::/
|     ノ     | |  |   \  /  )  /
ヽ    /     `ー'´      ヽ /    /
 |    |   l||l 从人 l||l      l||l 从人 l||l
 ヽ    -一''''''"~~``'ー--、   -一'''''''ー-、
  ヽ ____(⌒)(⌒)⌒) )  (⌒_(⌒)⌒)⌒))

じゃあ使ってみようか

    Employee emp = new Employee();
    emp.setAge(17).setEmpId(10)

setEmpId(10)まで書いた所で、IDE上でコンパイルエラー警告が出るはず。何でって?setAgeはPersonクラスの皮をかぶったインスタンスを戻すし、setEmpIdメソッドはPersonクラスでは定義されてないからね。

       ____
     /      \
   /  _ノ  ヽ、_  \
  / o゚((●)) ((●))゚o \  流れるようなインターフェイスって難しいお
  |     (__人__)    |
  \     ` ⌒´     /



       ____
     /      \
   /  _ノ  ヽ、_  \
  /  o゚⌒   ⌒゚o  \  静的型言語ってこんなことすら出来ないのかお。Javaはオワコン
  |     (__人__)    |  
  \     ` ⌒´     /


んなこと無い。ジェネリクス使おう。

public class Person <T>{
    private String name;
    private int age;

    public String getName() {
        return name;
    }
    public T setName(String name) {
        this.name = name;
        return (T) this;
    }
    public int getAge() {
        return age;
    }
    public T setAge(int age) {
        this.age = age;
        return (T) this;
    }
}
public class Employee extends Person<Employee> {

    private int empId;
    private String division;
    
    public int getEmpId() {
        return empId;
    }
    public Employee setEmpId(int empId) {
        this.empId = empId;
        return this;
    }
    public String getDivision() {
        return division;
    }
    public Employee setDivision(String division) {
        this.division = division;
        return this;
    }
}


呼び出しはこんな感じ。

    Employee emp = new Employee();
    emp.setAge(17).
      setEmpId(10).
      setName("H.P.Lovecraft").
      setDivision("Unspeakable Div.");

PersonのメソッドであるsetAgeやsetNameがEmployeeクラスのインスタンスを戻しているので、setEmpIdやsetDivisionを呼び出してもコンパイルエラーにならない。

          ____
       / ノ  \\
      / (●)  (●)\
    / ∪  (__人__)  \     凄く…微妙です
    |      ` ⌒´    |
     \ /⌒)⌒)⌒)   //⌒)⌒)⌒)
    ノ  | / / /   (⌒) / / / /
  /´    | :::::::::::(⌒)  ゝ  :::::::::::/
 |    l  |     ノ  /  )  /
 ヽ    ヽ_ヽ    /'   /    /
  ヽ __     /   /   / 

漏れもそう思う。

追記: AA記法を使っても何故か崩れまくるのは、はてなブログのせい?
再度追記:どうもはてなblog側でAA記法を上手く扱えてないっぽい(preタグのclassが変で、はてなダイアリーだと"ascii-art"クラスになるはずが"lang-"になってる。)
メニューの「管理」->「デザイン」->「カスタマイズ」->「デザインCSS」に以下のCSSを追加して対応。

pre.lang- {
    font-family:  "MS Pゴシック", "MS PGothic", Mona, IPAMonaPGothic, 'Textar', sans-serif!important;
    font-size: 12pt;
    line-height: 18px;
}