[ Java Gold 対策 ] はじめてのスレッド <よりみち2> : ラムダ式で書く。

シリーズ一覧
[Java Gold対策] はじめてのスレッド 1日目 : スレッドとは?
[Java Gold対策] はじめてのスレッド 2日目 : スレッドの作り方
[Java Gold対策] はじめてのスレッド 3日目 : スレッドの作り方2
[Java Gold対策] はじめてのスレッド <よりみち1> : 匿名クラスで書く。
[Java Gold対策] はじめてのスレッド <よりみち2> : ラムダ式で書く。 
[Java Gold対策] はじめてのスレッド 4日目 : スレッドをsleepさせよう
[Java Gold対策] はじめてのスレッド 5日目 : joinメソッドで順番待ち
[Java Gold対策] はじめてのスレッド 6日目 : runメソッドで条件付き停止

目次

もういっぽ、進んでみよう

前回は、匿名クラスでスレッドの生成をスッキリさせました。
もっと!スッキリ、わかりやすいコードにするために、ラムダ式で書いてみましょう。

なお、ラムダ式の詳細についてはこの記事では書ききれません…ごめんなさい。
(別シリーズで「ラムダ式攻略」を記事にしていけたらな、と計画中です…)

今回はあくまでも、「ラムダ式を使ったスレッドの生成を理解できる・書ける」ことを目標に進めていきます。
さっそく、頑張っていきましょう!

スレッドシリーズの匿名クラス回(=前回)はこちら
: 匿名クラスで書く。”>[Java Gold対策] はじめてのスレッド<よりみち1> : 匿名クラスで書く。

スレッドシリーズ初回はこちら
[Java Gold対策] はじめてのスレッド 1日目 : スレッドとは?

はじめまして、ラムダ式

ラムダ式は、メソッドの引数などに「処理そのものを渡す」ことができる、便利な書き方です。

今回のラムダ式は、前回の匿名クラスよりも、さらにド直球です。
インスタンス生成もしない。
関数の名前もない。
あるのは「実際の処理そのもの」だけ、です。

言うなれば、匿名クラスは「クラスの名前がない」のに対し、ラムダ式は「クラスもメソッドも名前を書かない」というものです。
そのため、匿名クラスよりもさらにスッキリしたコードになります

ラムダ式の構文

最低限の構文だけ見ておきましょう。

(引数) -> { 処理; }

これだけです!
逆にシンプルすぎてイメージできないですね…

たとえば、文字列を受け取ってそれを表示する場合、このように書けます。

実際のコード:

import java.util.function.Consumer;
public class LamdaTest {
    public static void main(String[] args) {
        Consumer<String> consumer = (String str) -> { System.out.println(str); };
        consumer.accept("Hello, Lamda!");
    }
}

実行結果:

Hello, Lamda!

ラムダ式の部分だけ抜き出し:

String型のstrという変数を宣言して、printlnメソッドの引数として渡しています。

この場合、このラムダ式自体の引数は「String型のstr」で、戻り値は「なし」です。

( String str ) →{ System.out.println( str ); }

今回は「acceptメソッド」や「Consumerクラス」についての説明を省きますが、ラムダ式のなんとなくの構文だけつかめればOKです!

ラムダ式がぼや~っとわかったところで、早速、スレッドのコードを書き直してまいりましょう!

具体的に見てみる。

変更前①:これまでの書き方(復習)

このコードはおなじみですね。
Runnableインターフェイスを実装した「名前を持った」クラスを用意して、そのインスタンスを他のクラスで生成しています。

// Runnableインターフェイスを実装した「有名」クラス
class SampleThread2 implements Runnable {		
	public void run() {		
		for(int i = 0; i < 10; i++) {
			System.out.println("SampleThread2のrunメソッド(" + i + ")");
		}
	}
}

public class SampleThreadMain2 {
	public static void main(String[] args) {
		SampleThread2 t = new SampleThread2();
		// SimpleThread2オブジェクトを引数にしてThreadオブジェクトを生成
		Thread thread = new Thread(t);
		thread.start();
		
		for (int i = 0; i < 10; i++) {
			System.out.println("SampleThreadMain2のmainメソッド(" + i + ")");
		}
	}
}

変更前②:匿名クラスVer(前回の復習)

これは前回のコードと全く同じものです。
匿名クラスを使えば、別にクラスを用意する必要がなくなるのでした。
Threadクラスのコンストラクタの引数の中で、直接Runnableインターフェイスを実装したインスタンスの生成を書いてしまう方法でした。

public class SampleThreadMain2 {
    public static void main(String[] args) {
    
        // Threadクラスのコンストラクタの引数の中で
	// Runnableインターフェイスの実装を書く
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("SampleThread2のrunメソッド(" + i + ")");
                }
            }
        });
        // ↑ここまでずっと、コンストラクタの引数
        
        thread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("SampleThreadMain2のmainメソッド(" + i + ")");
        }
    }
}

匿名クラスについて不安な方は、使い所などの説明もしているので、
こちらの記事を確認してください。

変更後:ラムダ式Ver

いよいよ、ラムダ式です!
下のコード、これまでとどこが変わったか、わかりますか?

public class SampleThreadMain2 {
    public static void main(String[] args) {
    
        // Threadクラスのコンストラクタの引数の中で
	  // 実際の処理を書く
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("SampleThread2のrunメソッド(" + i + ")");
            }
        });
        thread.start();

        for (int i = 0; i < 10; i++) {
            System.out.println("SampleThreadMain2のmainメソッド(" + i + ")");
        }
    }
}

Threadクラスのコンストラクタに渡す、引数の部分が更に短くなっています!
もう完全に「実際の処理」であるfor文ブロックだけですね。

「 ( ) 」は、ラムダ式内の処理で引数をなにも受け取らないことを示しています。
また、今回はprintlnメソッドを呼び出す処理ですので、戻り値もありませんね。

このコード、よく見てみると、不思議なことが起こっていますね。
ラムダ式の中には「Runnableインターフェイス」も「runメソッド」も書かれていません。
これでは、「どのインターフェイスのどのメソッドをオーバーライドするのか」が分からないのでは?と思われますよね。

実はこれ、Runnbaleインターフェイスが「関数インターフェイス」だからできることなんです。

関数型インターフェイスとは

関数型インターフェイスは、
抽象メソッドを「一つだけ」持つインターフェイスのこと。

ラムダ式は「関数型インターフェイス」と呼ばれるインターフェイスに対してのみ、使うことができます。

関数型インターフェイスでは、実装されるメソッドが1つしかありません。
したがって、名前を書かなくてもコンパイラーは「runメソッドね!」と見つけることができます
そのため、メソッド名を書かない、という指示の仕方が可能になっています。

逆に言うと、ラムダ式レベルの省略スタイルでは、関数インターフェイスという、これまたシンプルなインターフェイスを呼び出すことしかできないんですね。

関数型インターフェイスはこのシンプルさ故にとっても便利なので、いろんなパターンの「引数・戻り値」ですでに定義されています
プログラマーはそれらを必要に応じてラムダ式という形で実装することで、いろんな処理を省略することができます。
(詳しくは、ラムダ式を扱う回で理解していきましょう!)

ラムダ式=シンプルさを突き詰めた技

もう皆さんお気づきかと思いますが、実はこのラムダ式、
本質的には匿名クラスで行っていることと、変わりはありません!

難しく感じるかもしれませんが、シンプルさを突き詰めた一つの技法と割り切ってしまいましょう。
技法のひとつなので、理解よりは慣れが必要だと捉えて、たくさんのサンプルコードに出会うことをおすすめします。

今は地味…でもこのあと主役になる。

ちょっと行数は減ったけど、そんなにすごいのかな? 逆に分かりづらくなってないかな?

そう思った方、その気持はよくわかります。
確かに、今回のような短いスレッドコードではそこまで便利さを感じられないかもしれません…汗

しかし、ラムダ式は範囲を問わず、いろいろなインターフェース、クラスで使うことができる技法です。
少しの量なら実感できなくても、大量・多様になっていったら少しの差が大きくなっていきます。
実際の開発現場では当たり前に多用される「基本文法」の一つですので、今からゆっくり慣れていきましょう!

ちなみに、このラムダ式の凄さが一番実感できるのは「Stream API」です。
更にちなむと、StreamAPIはOracle Java Gold資格試験の中でも最重要・難関と言われる範囲です。
(StreamAPIについては、別シリーズで記事にしたいと思っております…)

まとめ・次回予告

皆さんここまで<よりみち>お疲れ様でした!
匿名クラス・ラムダ式は奥が深すぎるテーマでしたので、さら~っと説明してきましたが、いかがでしたでしょうか?

もしこの<よりみち>の内容がわからなくても、スレッドの生成は可能ですので問題ありません!
理解する、というよりも、一回飲み込んで数をこなしていけば、いつの間にか腑に落ちていることもあります。
なので、気を取り直して、続けていきましょう

さて、よりみちは今回で終了です。
次回は、本編に戻って、スレッドの制御方法についての便利なメソッドを説明していきます。
お楽しみに

(Visited 51 times, 1 visits today)
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

株式会社NEUGATEは、都内で企業研修や職業訓練を運営している会社です。主に、IT系の教育事業に力を入れています。
この記事は、株式会社NEUGATEの教育事業部が執筆をしています。

企業ホームページ:https://neugate.co.jp/

コメント

コメントする

目次