Javaラムダ式がオーバーロードで定義されるとき、どんな動きをするのか
Javaのラムダ式はいままでのJavaの文法と違いかなり省略した書き方ができる。型を省略した書き方でメソッドがオーバーロード時ちゃんと呼ばれるか検証してみた。
メソッドがひとつしかないとき
Main.java
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.methodA(e -> "{" + e + "}");
}
void methodA(UnaryOperator<String> op){
System.out.println("UnaryOperator<String> が呼ばれました");
System.out.println(op.apply("MethodA"));
}
}
実行結果
UnaryOperator<String> が呼ばれました
{MethodA}
ひとつしかないし、正しく呼ばれるのも当たり前かな
メソッドが複数定義されている場合(違う関数型インターフェースを使用した場合)
Main.java
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.methodA(e -> "{" + e + "}");
main.methodA(e -> System.out.println(e));
}
void methodA(UnaryOperator<String> op){
System.out.println("UnaryOperator<String> が呼ばれました");
System.out.println(op.apply("MethodA"));
}
void methodA(Consumer<String> con){
System.out.println("Consumer<String> が呼ばれました");
con.accept("MethodA");
}
}
実行結果
UnaryOperator<String> が呼ばれました
{MethodA}
Consumer<String> が呼ばれました
MethodA
ラムダ式の何を引数にとって何の型を返すのかというのを自動的に判断してちゃんと選ばれたメソッドが呼ばれるようだ。
あえてUnaryOperatorとFunctionで同じ動きをさせてみる
UnaryOperatorは入力Tを入力して同じ型のTを返す関数型インターフェースで、FunctionはTを入力して違う型Rを返す関数型インターフェースである。あえてFunctionで同じ型を返すように定義すれば理論上どちらのメソッドでも適用可能である。その場合を検証してみた。
Main.java
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.methodA(e -> "{" + e + "}");
}
void methodA(UnaryOperator<String> op){
System.out.println("UnaryOperator<String> が呼ばれました");
System.out.println(op.apply("MethodA"));
}
void methodA(Function<String, String> func){
System.out.println("Function<String, String> が呼ばれました");
System.out.println(func.apply("MethodA"));
}
}
実行結果
UnaryOperator<String> が呼ばれました
{MethodA}
両方でも解釈できそうなのでエラーになるかなと思ったものだけど、UnaryOperatorが呼ばれました。e -> "{" + e + "}"というラムダ式がUnaryOperatorに近いからそっちの方が呼ばれたのかな?
ちなみにに、あえて逆の方を呼び出したい時はキャストすればいいらしい
Main.java
public class Main {
public static void main(String[] args) {
Main main = new Main();
main.methodA((Function<String, String>)e -> "{" + e + "}");
}
void methodA(UnaryOperator<String> op){
System.out.println("UnaryOperator<String> が呼ばれました");
System.out.println(op.apply("MethodA"));
}
void methodA(Function<String, String> func){
System.out.println("Function<String, String> が呼ばれました");
System.out.println(func.apply("MethodA"));
}
}
実行結果
Function<String, String> が呼ばれました
{MethodA}
同じ入力、戻り値の関数型インターフェースを自力で定義してオーバーロードしちゃう
Main.java
public class Main {
@FunctionalInterface
interface myInterface<T>{
T apply(T t);
}
public static void main(String[] args) {
Main main = new Main();
main.methodA(e -> "{" + e + "}");
}
void methodA(UnaryOperator<String> op){
System.out.println("UnaryOperator<String> が呼ばれました");
System.out.println(op.apply("MethodA"));
}
void methodA(myInterface<String> my){
System.out.println("myInterface<String> が呼ばれました");
System.out.println(my.apply("MethodA"));
}
}
流石にこれはコンパイルエラーになりました。入力の型と戻り値の型が完全に一致しているので判断できないっぽい。
なお、こうなってしまった場合先ほどのようにキャストすればOK
main.methodA((UnaryOperator<String>)e -> "{" + e + "}");
//もしくは
main.methodA((myInterface<String>)e -> "{" + e + "}");
そもそもキャストしなければならないラムダ式なんてラムダ式のメリットが薄くなっているので、こんな設計にしないようにすべきです。