メソッド参照

メソッドの参照をラムダ式の代わりに指定することも可能だ。たとえば以下のサンプルコードではforEach()メソッドの引数にSystem.outのprintln()メソッドの参照を渡している。

List<String> list = Arrays.asList("Java", "Scala", "Groovy");
list.forEach(System.out::println);

上記の2行目は以下のコードと同じ意味になる。

list.forEach((s) -> System.out.println(s));

メソッド参照はラムダ式同様、関数型インタフェースの型を持つ変数への代入が可能だ。

Consumer<String> c = System.out::println;
c.accept("Hello, World!");

実質的final

従来のJavaで匿名クラスを使用したことのある方であればおわかりかと思うが、ラムダ式の中から外部の変数を参照する場合、本来はfinal修飾子が付与されている変数しか参照することができない。

しかし、Java 8では宣言時に値が代入され、以降変更されていない変数を実質的にfinalとみなすことができるようになっている。ラムダ式や匿名クラスの内部からはfinal修飾子が付与されていない変数も参照可能だ。

public void outer(String message) {
  Runnable r = () -> {
    // ラムダ式の中からouterメソッドの引数を参照可能
    System.out.println(message);
  }
}

ただし、以下のように変数の値を変更している場合は実質的finalと扱うことができないため、ラムダ式の内部から参照しようとするとコンパイルエラーとなってしまう。

public void outer(String message) {
  Runnable r = () -> {
    System.out.println(message);
  }
  // 変数を再代入するとコンパイルエラーになる
  message = "hoge";
}

実質的finalはいちいちfinal修飾子をつけなくてもよいので便利だが、このような背景を理解していないとコンパイルエラーになるケースで原因がわからず苦労することがあるかもしれないので注意して欲しい。