答えは (私の驚きと満足に) YES です。私は自分でこの質問に答えました: メソッド呼び出しが問題のクラスのインスタンスを返す場合、これは少しの作業で実行できます (以下の chainable を参照)。トップレベルのソースを編集できる場合、これを行うさらに簡単な方法も見つけました。
最上位クラス (A) では:
protected final <T> T a(T type) {
return type
}
C が B を拡張し、B が A を拡張するとします。
呼び出し:
C c = new C();
//Any order is fine and you have compile time safety and IDE assistance.
c.setA("a").a(c).setB("b").a(c).setC("c");
例 1 と 3 は、既存のクラス階層を流暢にする方法であり、既存のクラスが流暢である限り、メソッドを任意の順序で呼び出せるようにする方法です (ただし、ソースにアクセスできないか、ソースを変更することはできません)。WAY2 は、ソースにアクセスでき、呼び出しをできるだけ単純にしたい例です。
完全な SSCCE:
import static java.lang.System.out;
public class AATester {
public static void main(String[] args){
//Test 1:
for(int x: new int[]{ 0, 1, 2 } ){
A w = getA(x);
//I agree this is a nasty way to do it... but you CAN do it.
Chain.a(w.setA("a1")).a(w instanceof C ? ((C) w).setC("c1") : null );
out.println(w);
}
//Test for WAY 2: Hope this wins Paul Bellora's approval
//for conciseness, ease of use and syntactic sugar.
C c = new C();
//Invoke methods in any order with compile time type safety!
c.setA("a2").a(c).setB("b2").a(c).set("C2");
out.println(w);
//Example 3, which is Example 1, but where the top level class IS known to be a "C"
//but you don't have access to the source and can't add the "a" method to the
//top level class. The method invocations don't have to be as nasty as Example 1.
c = new C();
Chain.a(c.setA("a3")).a(c.setB("b3")).a(c.setC("c3"));//Not much larger than Example 2.
out.println(w);
}
public static getA(int a){//A factory method.
A retval;//I don't like multiple returns.
switch(a){
case 0: retval = new A(); break;
case 1: retval = new B(); break;
default: retval = new C(); break;
}
return retval;
}
}
テスト クラス A
public class A {
private String a;
protected String getA() { return a; }
//WAY 2 - where you have access to the top level source class.
protected final <T> T a(T type) { return type; }//This is awesome!
protected A setA(String a) { this.a=a; return this; }//Fluent method
@Override
public String toString() {
return "A[getA()=" + getA() + "]";
}
}
テスト クラス B
public class B extends A {
private String b;
protected String getB() { return b; }
protected B setB(String b) { this.b=b; return this; }//Fluent method
@Override
public String toString() {
return "B[getA()=" + getA() + ", getB()=" + getB() + "]\n "
+ super.toString();
}
}
テスト クラス C
public class C extends B {
private String c;
protected String getC() { return c; }
protected C setC(String c) { this.c=c; return this; }//Fluent method
@Override
public String toString() {
return "C [getA()=" + getA() + ", getB()=" + getB() + ", getC()="
+ getC() + "]\n " + super.toString();
}
}
チェーンクラス
/**
* Allows chaining with any class, even one you didn't write and don't have
* access to the source code for, so long as that class is fluent.
* @author Gregory G. Bishop ggb667@gmail.com (C) 11/5/2013 all rights reserved.
*/
public final class Chain {
public static <K> _<K> a(K value) {//Note that this is static
return new _<K>(value);//So the IDE names aren't nasty
}
}
チェーンのヘルパー クラス。
/**
* An instance method cannot override the static method from Chain,
* which is why this class exists (i.e. to suppress IDE warnings,
* and provide fluent usage).
*
* @author Gregory G. Bishop ggb667@gmail.com (C) 11/5/2013 all rights reserved.
*/
final class _<T> {
public T a;//So we may get a return value from the final link in the chain.
protected _(T t) { this.a = t }//Required by Chain above
public <K> _<K> a(K value) {
return new _<K>(value);
}
}
出力:
A [get(A)=a]
B [get(A)=a, getB()=null]
A [getA()=a]
C [getA()=a, getB()=null, getC()=c)]
B [get(A)=a, getB()=null]
A [get(A)=a]
QED。:)
これを行う人を見たことがありません。これは新しい、潜在的に価値のある技術になると思います。
PS 「elvis様の使い方」に関しては、1本か2本 vs 8本以上です。
本 b = null;
パブリッシャー p = null;
書籍のリスト = null;
String id = "メルニボーンのエルリック";
books = Chain.a(b = findBook(id)).a(b != null ? p = b.getPublisher() : null)
.a(p != null ? p.getPublishedBooks(): null).a;
out.println(books==null ? null : Arrays.toString(books.toArray()));
対:
本 b = null;
パブリッシャー p = null;
書籍のリスト = null;
String id = "メルニボーンのエルリック";
b = findBook(id);
配列 [] 本 = null;
if( b != null ) {
p = b.getPublisher();
if(p != null) {
本 = p.getPublishedBooks();
}
}
out.println(books==null ? null : Arrays.toString(books.toArray()));
NPE はありません。チェーンが完了すると、"Elric of Melnibone" の出版社から出版されたすべての本 (つまり、"Ace" 出版社が出版したすべての本) を取得し、そうでない場合は null を取得します。