機能(関数)をクラス内に実装せずに、外部に委譲することがある。単純な例ではこんな感じ。
public class Action{ private Runnable action; public void setAction(Runnable action){this.action = action;} public void action(){ if(action != null) action.run(); } }
Runnableのように引数を取らないような関数なら別に問題ないが、大概の場合は委譲元のインスタンスを引数にとりたい。
すると、こうなる。
public class Action{ private Consumer<Action> action; public void setAction(Consumer<Action> action){this.action = action;} public void action(){if(action!=null)action.accept(this);} }
委譲は継承が面倒
機能の委譲をしている場合でも、データの拡張などでクラスを継承する方が合理的な場合もある。
public class Action{ private Consumer<Action> action; public void setAction(Consumer<Action> action){this.action = action;} public void action(){action.accept(this);} } class ActionB extends Action{ public int b; }
委譲した関数が、ActionBにしか対応する必要がない場合、このままだと一々キャストが発生する。
actionB.setAction(a->{ ActionB b = (ActionB)a;//キャストめんどい System.out.println(b.b); })
かといって<? extends Action>にしておくのも上手くない。
public class Action{ private Consumer<? extends Action> action; public void setAction(Consumer<? extends Action> action){this.action = action;} public void action(){if(action!=null)((Consumer)action).accept(this);} } public class ActionB extends Action{ public int b; } public class ActionC extends ActionB{ public int c; } ActionB actionB; actionB.setAction((ActionB b)->System.out.println(b.b));//ActionBの型を書くのめんどい actionB.setAction((ActionC c)->System.out.println(c.c));//これも通る
安全だし、ラムダで型を書かなくて良い楽な方法もあるにはある
ただし、マジでめんどい
public class Action <V extends Action<?>>{ private Consumer<? super V> action; protected void setAction(Consumer<? super V> action){this.action = action;} public void action(){if(action!=null)((Consumer)action).accept(this);} } class ActionB<V extends ActionB<?>> extends Action<V>{//<V extends ActionB<?>>がめんどい int b; } class ActionC<V extends ActionC<?>> extends ActionB<V>{//<V extends ActionC<?>>がめんどい int c; } ActionB<?> actionB = new ActionB<>();//<?>と<>を書くのがめんどい actionB.setAction(b->System.out.println(b.b));//ラムダ式は楽 actionB.action();
ならば、こう書ければ楽なはずだ
そもそも人間がガンガるから面倒くさいのだ。コンパイラが頑張れば良いのだ。
たとえば、以下のようなアノテーションとジェネリックを書いておけば、Setter,Getter,メソッドコールの部分は自動的にJavaコンパイラが頑張ってくれればいいんじゃないのかな。
public class Action{ @TransferMethod //委譲メソッドであること宣言する的な private Consumer<this> action; } public class ActionB extends Action{ public int b; } ActionB actionB = new ActionB(); actionB.setAction( b -> System.out.println(b.b) ); actionB.action();
lambda修飾子があればもっと楽なはずだ
だが、ここまで考えて気がついたのだが、Consumer<this>も書くのがめんどい。
Consumerならまだ楽だが、引数が増えたときは考えたくない。
コンパイラが頑張るなら、もはやこれで良いはずだ。
public class Action{ public lambda void action(); } public class ActionB extends Action{ public int b; } ActionB actionB = new ActionB(); actionB.action = b -> System.out.println(b.b) ; actionB.action();
なんと、実に楽だ。ついでにdefaultも使えるとさらに良いかもしれない。
public class Action{ public default lambda void action(){System.out.println("Action");} }
マルチスレッド対応のためにvolatileとかsynchronizedとか修飾できても楽しいかも知れない。
というわけで、nativeとか、abstractみたいな感じで、lambda修飾子があるとラムダがよりいっそう楽しそうな気がするのですが、どうでせうか。Lombokみたいな感じで実現できないだろうか?
絶対、どっかの誰かが提案してるんじゃないかな?