猫と一緒にガジェットライフ♪ムチャです。
JavaSE8 Goldアップグレード試験に向けて、勉強したことをまとめていきたいと思います。
写真はやっと発売されたJavaSE8 Goldの参考書です。でも自分が受けるのは7からのアップグレード試験(1Z0-810)なのでちょっと違うのですけどね。
まずは試験範囲のほとんどを占めている、ラムダ式の記述法からいきます。
ラムダ式で変わること
ラムダ式の導入で便利になる事はいろいろあるのですが、そのうちの1つとして、Java7まではわざわざ匿名クラスを記述して書いていた内容がシンプルにできるということです。Javaでは処理そのものを引数として渡すことができないため、たとえ処理が1行ですむとしてもクラスを定義して渡すしかできませんでした。
例えばスレッドを生成して何かさせたいときに、これまでは無名クラスを定義して以下のように書いていました。
new Thread(new Runnable(){ @Override public void run(){ System.out.println("ぬるぽ!"); } }).start();
他にはSwingでGUIプログラムを書くときに、コンポーネントに対しActionListenerを実装したクラスを渡すのにも使ったりします。
処理が長い場合はちゃんとクラスを1つ定義した方がよいですが、数行なら匿名クラスで住ますことが多いです。
Threadクラスの生成時にRunnableインタフェースを実装したクラスが必要なので、匿名クラスを生成してrunメソッドを実装します。
これがJava8でラムダ式を使った記述になるとこのようになります。
new Thread( () -> System.out.println("ぬるぽ!") .start();
実行結果
ぬるぽ!
とてもシンプルになりますね。
どんなインタフェースでも置き換えられるわけではないので、その辺の話は徐々に説明していこうと思います。
ラムダ式の記述法
基本は以下の通りです。(引数リスト) -> { 関数本体; }
例えば、CollectionsクラスのsortメソッドはComparatorインタフェースを引数に取ります。
//Collections#sortメソッド定義 public static <T> void sort(List<T> list, Comparator<? super T> c)
これはラムダ式を使うと次のように書けます。
リストを文字列の長さが小さい順にソートする処理です。
List<String> strs = Arrays.asList(new String[]{"日本","アメリカ","カナダ"}); Collections.sort(strs, (String s1, String s2) -> { return s1.length() - s2.length(); }); System.out.println(strs);
実行結果
[日本, カナダ, アメリカ]
Java8のラムダ式は「関数型インタフェース」という、メソッドが1つだけ定義されたインタフェースだけが記述できると決まっています。従って、呼び出すべきメソッドは判断できるのでメソッド名は省略できます。
※関数型インタフェースは別の回に書きます。
引数の型は前後の文脈からコンパイルが判断してくれるので省略できます。
Comparator<String> c = (s1, s2) -> { return s1.length() - s2.length(); }
Comparator<String> c = (s1, String s2) -> { return s1.length() - s2.length(); } //→コンパイルエラー
このときreturn文のみになる場合はreturnは省略しなくてはいけません。
Comparator<String> c = (s1, s2) -> return s1.length() - s2.length(); //→コンパイルエラー Comparator<String> c = (s1, s2) -> s1.length() - s2.length(); //→OK
シンプルですね!
もちろん複数の文になる場合は中括弧でくくってブロックにしないといけません。
Comparatorc = (s1, s2) -> { System.out.println("なんかの処理"); //: return s1.length() - s2.length(); //return文は必須 };
別のケースになりますが、引数が1個の場合はカッコも省略できます。
Consumerという、引数を1個受け取って値を返さない関数型インタフェースがありますが、以下のように書けます。
Consumer<String> c = n -> System.out.println(n);
ただし引数無しの場合はカッコだけ書かないといけません。
Runnable r = () -> System.out.println("ぬるぽ!");
戻り値についてですが、当然ながらvoidの場合にreturnを書いてはいけませんし、戻り値があるのに関数本体が値を返していない場合はコンパイルエラーになります。
詳しくはまた関数型インタフェースの回に書きます。
Comparator<String> c = (s1, s2) -> { System.out.println(s1); }; //→コンパイルエラー Consumer<String> c = s -> {return s.length(); } //→コンパイルエラー Consumer<String> c = s -> s.length(); //省略していればOK
Runnable r = () -> 1; //→戻りがvoidのメソッドなのに省略により値をreturnしてる事になるのでコンパイルエラー
さらに省略した「メソッド参照」という書き方もあるのですが、それはまた別途記事にします。
今回のまとめ
- ラムダ式によって処理そのものを引数として渡したり変数に入れたりできる(ように書ける)
- 型はコンパイラが推論してくれるので省略できる
- 省略できないケースに気をつける(引数無しの時のカッコ、複数文の最後のreturn文)
実際にコードを書くときはeclipseなどのIDEを使うと思うので、おかしければ警告出してくれるから完璧に覚える必要は無いでしょう。
試験ではコードが出てきて何が出力されるか・コンパイルエラー・実行時エラーの選択肢から選ぶパターンの問題があるので、覚えておきましょう。
これから試験合格までこんな感じでまとめていこうと思っています。
共通のラベルとして「JavaSE8Gold」を付けていきます。
よろしければおつきあいくださいませ。
それではみなさまよきガジェットライフを(´∀`)ノ