#導入
こんにちは。けちょんです。
皆さん、メソッド参照使っていますか?
便利だし、可読性も上がりますよね。
そんなメソッド参照で。気を付けるべき仕様があったので紹介します。
#よくやる使い方
public class Main {
public static void main(String[] args) throws Exception {
Arrays.asList("test1", "test2").stream().forEach(System.out::println);
}
}
// test1
// test2
staticメソッドをメソッド参照していますね。
他にもこんな使い方ができますね。
public class Main {
public static void main(String[] args) throws Exception {
Arrays.asList("test1", "test2").stream().map(String::toUpperCase).forEach(System.out::println);
}
}
// TEST1
// TEST2
StringクラスのtoUpperCaseを呼び出していますね。
見やすくて便利です。
#インスタンスメソッドの呼び出し方
また、上ではstaticメソッドを紹介していますが、インスタンスメソッドも使用できます。
class Customer {
public String getName() {
return "which1";
}
}
public class Main {
public static void main(String[] args) throws Exception {
Arrays.asList(new Customer(), new Customer()).stream().map(Customer::getName).forEach(System.out::println);
}
}
// which1
// which1
同じ記法で書けるんですね。
#つまり
以下は同じCustomer::getName
で書けることになります。
map(e -> Customer.getName(e)) // staticメソッドを呼び出す場合
map(e -> e.getName()) // 格納されたインスタンスのインスタンスメソッドを呼び出す場合
#ここで疑問
参照できるメソッドが両方あったらどうなるの?
同じ記法でstaticメソッドとインスタンスメソッドを呼び出せることは紹介しましたが、両方あった場合、どちらが使用されるのでしょうか。
class Customer {
public String getName() {
return "which1";
}
static public String getName(Customer customer) {
return "which2";
}
}
public class Main {
public static void main(String[] args) throws Exception {
Arrays.asList(new Customer(), new Customer()).stream().map(Customer::getName).forEach(System.out::println);
}
}
- staticメソッド
- インスタンスメソッド
- コンパイルエラー
- 実行時エラー
↓
↓
↓
↓
↓
正解は、3のコンパイルエラーです!
このようなエラー文が出ます。
Ambiguous method reference: both getName() and getName(Customer) from the type Customer are eligible
日本語訳:
あいまいなメソッド参照:タイプCustomerのgetName()とgetName(Customer)の両方が適格です
どちらを参照すべきかわからないため、コンパイルエラーを出していますね。
#結局何を気を付けるべき?
今回は両方のメソッドが参照できるためコンパイルエラーを吐きました。
ただし、以下のように一目では分かりにくい、参照できないパターンがあります。
- メソッドが可視性により参照できなかった場合
- メソッド名が微妙に異なる場合
- メソッドの引数が異なる場合
などなど
どちらのメソッドが参照されるのか、開発者の想定外の動きをする可能性があります。
#まとめ
開発者側で実装した型を用いたオブジェクトを扱う場合、メソッド参照は使用を控えた方が無難