1. xrdnk

    Posted

    xrdnk
Changes in title
+[Java] ラムダ式入門
Changes in tags
Changes in body
Source | HTML | Preview

緒言

Javaでスレッドを実行するためには,Threadクラスを継承したサブクラスを用いる方法と,
Runnableインタフェースを実装したサブクラスを用いる方法があります.
今回,ラムダ式の簡易的な説明のために後者を例にします.

Function.java
public class Function implements Runnable{
    @Override
    public void run() {
        // スレッドで処理する内容
        System.out.println("hello!");
    }
}
Main.java
public class Main {
    public static void main(String[] args) {
        Thread thread = new Thread(new Function());
        thread.start();
        System.out.println("finish!");
    }
}

出力はこんな感じになると思います.

finish!
hello!

ThreadクラスとRunnableインタフェースの関係は
GoFデザインパターンのStrategyパターン設計が使われています.
Strategyパターンは大まかにいえば「付け替えできるアルゴリズムを実現する」設計で,
もともと1つだったクラスを処理の全体の流れを担当するクラスと,
具体的なアルゴリズムを担当するクラスに分けて考えます.
Strategyパターンを使うことで,使う側(Threadクラス)と
使われる側(Runnableインタフェースを実現したクラス)を
直接関係せずに済むため,あとから付け替えできるアルゴリズム実現することができます.
(Strategyパターンを詳しく!という方はこちらを見ると良いかも)
UML2.png
しかしながら,インタフェースを実現したクラスには複雑なもの(UML図のFunctionHardクラス)と
簡単なコードで済むクラス(UML図のFunctionEasyクラス)のようなものがあります.

簡単なコードで済むのに,逐一新しいクラスを
定義しなくてはならないのめんどくないですか?ということです.

ラムダ式の効力

緒言で提示した問題点を解決するために,Java SE8から導入されたラムダ式を利用します.
以下のコードはFunctionEasyクラスを作る代わりにラムダ式に置換したものです.

Lambda.java
public class Lambda {
    public static void main(String[] args) {
        Runnable r = () -> System.out.println("hello!");
        Thread thread = new Thread(r);
        thread.start();
        System.out.println("finish!");
    }
}

このように1行で済みます.スッキリ!
ラムダ式は「関数型インタフェースをインスタンス化するときに
めんどい記述しなくても済むような記法」と思えばよいです.

実装が必要なメソッドを1つだけ持つインタフェースを「関数型インタフェース」や
「SAM(Single Abstarct Method)インタフェース」と呼んだりします.

SAMということからわかる通り,抽象メソッドを1つだけ持つため,
ラムダ式がどのメソッドを実装するのか?引数と戻り値はどうなのか?
推論が可能になります.逐一メソッド名を決める必要がないです.
インタフェースを実現したクラスを用意せずに,ポリモーフィズムを実現できます.

匿名クラスとラムダ式

Java8以前は匿名クラスで実現されていました.
匿名クラスは無名クラスとも呼ばれることがあります.

        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("hello!");
            }
        }

長くて可読性が悪いですね.ほとんど誰も使わない状態になりました.
そこでラムダ式が登場し,これをもっと簡単に記述できるようになります.

        Runnable r = () -> {
            System.out.println("hello!");
        }

ラムダ式の宣言は,以下のように引数の変数宣言と処理ブロックで構成されます.

     { 引数 } -> { 処理 };

「->」をアロー演算子と呼びます.処理ブロックの最後にセミコロンをつけるの忘れずに.

ラムダ式省略記法

ラムダ式では省略できるものがあります.
以下の原型を例にたどってみましょう.

原型
        Sample sample = (String a) -> { System.out.println(a);};

引数が1つのときだけの場合,カッコ「()」を省略できます.
また,引数の型は型推論できるため,省略することができます.

省略記法1
        Sample sample = a -> { System.out.println(a);};

また,メソッド本体が1行の場合は,中カッコ「{}」と文末のセミコロン「;」を省略できます.
return文の場合はreturnも省略できます.

省略記法2
        Sample sample = a -> System.out.println(a);

さらに,引数が推論できる場合,メソッド呼び出しは
 クラス名::メソッド名
 オブジェクト名::メソッド名
のように省略することができてしまいます.

省略記法3
        Sample sample = System.out::println;