2016年7月24日日曜日

[JavaSE8 Goldへの道] その1 ラムダ式の記述法


猫と一緒にガジェットライフ♪ムチャです。

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(); } //→コンパイルエラー

関数本体は1文の場合中括弧を省略できます。
このときreturn文のみになる場合はreturnは省略しなくてはいけません。
Comparator<String> c =
  (s1, s2) -> return s1.length() - s2.length(); //→コンパイルエラー
Comparator<String> c =
  (s1, s2) -> s1.length() - s2.length(); //→OK

シンプルですね!
もちろん複数の文になる場合は中括弧でくくってブロックにしないといけません。
Comparator c = (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」を付けていきます。
よろしければおつきあいくださいませ。

それではみなさまよきガジェットライフを(´∀`)ノ





▼こちらの記事もどうぞ

▼ブログを気に入っていただけたらRSS登録をお願いします!
▼ブログランキング参加中!応援よろしくお願いします。

スポンサーリンク