Java SE 8 の新機能、使ってみましたか?

Java SE 8 の新機能、使ってみましたか?

Java SEの最新バージョン、Java SE 8がリリースされて早くも2年が経ちました。改めてJava SE 8で導入されたラムダ式について紹介したいと思います。これから学習しようと思っていた方は是非!

はじめに

2014年3月18日にリリースされた Java SE 8 は現在、マイナーアップデートを重ね、「Java SE 8 Update 92」 が最新となっています。(2016年6月1日現在)

そこで、このタイミングで Java SE 8の新機能 などについて再度確認をしてみたいと思います。

Java SE 8新機能

Java SE 8 はJ2SE 5.0(Java SE 5.0)以来の革新的なバージョンアップ と言われています。そのJava SE 8での新機能トピックとしては、

  • Project Lambda
  • Type Annotation (JSR 308)
  • Date and Time API (JSR 310)
  • Nashorn JavaScript エンジン

などが挙げられます。その中でもやはり目玉となっているのが 「Project Lambda」 です。

今回はこのProject Lambdaの核となる 「ラムダ式」 について紹介していきたいと思います。

Project Lambda

Project Lambda は名前の通り 「ラムダ式」 が中心となりそのラムダ式を利用する 「Stream API」 が含まれます。

Project Lambdaの導入背景には、動作環境の 「マルチコアCPUへの対応」 が挙げられます。マルチコアCPUにおいて性能をしっかりと使い切る、パラレル処理 の実装を一から行うのは簡単ではありません。

そこでパラレル処理を実装できるライブラリとして提供されているのが「Stream API」であり、またそのStream APIを利用する文法として「ラムダ式」が導入されています。

ラムダ式 概要

ラムダ式 は 「関数型インタフェースの実装を簡略化するための構文」 と言えます。そもそも関数型インタフェースとは何か?となるかと思いますが、関数型インタフェース とは 「オーバーライドするべきメソッドが1つのみのインタフェース」 のことです。

  • 有名なもので言うと、
    スレッドで利用する java.lang.Runnableインタフェース
    → run() メソッドのみ定義
  • GUIのイベント処理を行う java.awt.event.ActionListenerインタフェース
    → actionPerformed() メソッドのみ定義

などが関数型インタフェースに当てはまります。

これらの関数型インタフェースの実装を行う際に ラムダ式 を使用し、Stream APIの利用 に繋げていきます。ラムダ式を使用すると 局地的に関数型プログラミングが可能になる というイメージです。

また、Java SE 8からは関数型インタフェースを定義する際は、@FunctionalInterface というアノテーションが提供されていますので、インタフェースの先頭に注目して頂くと分かりやすいかと思います。

※ @FunctionalInterfaceは必須ではありませんが、指定しておくと コンパイラのチェック が入りますので、便利です。

//関数型インタフェース
@FunctionalInterface
public interface Player {
  // オーバーライドすべきメソッドが1つだけ!
  public abstract void play();
}

ラムダ式 適用範囲

では、実際に ラムダ式 を どのように定義していくのか 確認してみましょう。

上記、関数型インタフェースである Player インタフェースを実装したオブジェクトを生成したいとします。

その際どのような定義を行うでしょうか?
従来、一般的な定義方法としては 「匿名クラス」 を使用したインスタンス化です。

インタフェース型のオブジェクトを生成しつつ、オーバーライドすべき play() メソッド の実装を定義する方法です。
ただし、「匿名クラスを使用した定義」は コードの可読性 が低くなりがちです。そこで、このようなコードを ラムダ式で簡潔に書き替え たいと思います。

ラムダ式 構文

ラムダ式については 「型推論の強化」 というキーワードも押さえておいて頂きたいです。従来のJava言語というものは、「型をきちんと定義する!」 という印象が強かったのですが、このラムダ式では、「推論出来る部分は省略してOK」 というイメージです。

// 匿名クラスを利用した方法
Player p = new Player () {
  public void play() {
    System.out.println("Play!!");
  }
};
p.play();

つまり、上記コード記述したように
2行目・・・左辺の変数より型を推論できるため省略可
3行目・・・関数型インタフェースのため、定義するメソッドも推論できるため省略可

つまり、ラムダ式で定義する場合は、(引数リスト) -> { 処理 } という定義まで簡略化することが出来ます。
ラムダ式で使用する -> の演算子は 「アロー演算子」 と呼ばれます。

// ラムダ式を利用した方法
Player p =  () -> {
    System.out.println("Play!!");
};
p.play();

あわせて、メソッドの定義パターンによっては、より省略 することができます。

・メソッド内の処理が1行の場合 ・・・ { } や returnキーワードの省略可
→ ただし、{} とreturnは まとめて省略 すること。どちらかだけ残すことはできません。

・メソッド引数が1つだけの場合・・・ () の省略可
→ 今回の例のplay()は引数「なし」のメソッドのため、省略は不可

ラムダ式の注意点

上記で紹介しました通り、ラムダ式では引数の型や { } を 省略「出来すぎる」 点もあるので、慣れるまではある程度省略せずに記述する方法が良いかと思います。
※ 下記コードのように、変数 e の型が分かりにくくなる可能性もあるので、注意が必要です。

最後に

今回はラムダ式の基本構文について紹介させて頂きました。今回の内容では 「関数型インタフェースの実装を簡略化できる」 というイメージを持たれたと思いますが、このラムダ式は「Stream API」と組み合わせることでより幅広く利用することが出来ます。

ラムダ式、StreamAPI については、Java SE 8の目玉でもあるため、Oracle 社で提供している Javaプログラマ試験 でももちろん 試験範囲 となります。

次回は、このラムダ式を利用した Stream API についても紹介していきたいと思います。

記事は、予告なく変更または削除される場合があります。
記載された情報は、執筆・公開された時点のものであり、予告なく変更されている場合があります。
また、社名、製品名、サービス名などは、各社の商標または登録商標の場合があります。