注目記事

【Javaコード入門】継承、インターフェイス_⑤ラムダ式を利用する

(type args, …) -> { statements }
 type:引数の型
 args:引数の名前
 statements:処理本体

ラムダ式はJava 8から導入された構文で、大雑把にいうならば、メソッドをシンプルに表すための構文です。
例えば、「無名クラスを定義する」項の無名クラスをラムダ式で書き換えた例です。

AnonymousSample.java

  1. package com.example.mynavi.object;
  2.  
  3. public class AnonymousSample {
  4.  public static void main(String[​] args) {
  5.   Printable p = () -> {
  6.    System.out.println("TEST");
  7.   };
  8.   p.print(); // 結果:TEST
  9.  }
  10. }

ラムダ式で実装できるのは、関数型インターフェイスのメソッドです。この例であれば、Printableインターフェイスのprintメソッドをラムダ式で実装しているわけです。

 

関数型インターフェイス
関数型インターフェイスとは、メソッドを一つしかもたないインターフェイスのことです。@FunctionalInterfaceアノテーションを利用して、明示的に宣言することもできます。

 

  1. @FunctionalInterface
  2. public interface IPrintable {
  3.  void print();
  4. }

@FunctionalInterfaceアノテーションを付与した場合、インターフェイスがメソッドを複数持つなど、関数型インターフェイスの要件を満たさない場合に、コンパイルエラーを返すことができます。

ラムダ式のさまざまな記法

ラムダ式は特定の条件を満たす場合に、さまざまな省略記法を用いることができます。たとえば、以下のようなラムダ式があったとします。

  1. SampleInterface hoge = (String str) -> {
  2.  return str + str;
  3. };

これは、まず以下のように書き換えることができます。

  1. SampleInterface hoge = (str) -> {
  2.  return str + str;
  3. };

ラムダ式では引数の型をインターフェイスの定義から推論しますので、省略できます。これを型推論と言います。
そして、引数がひとつの場合には、引数を括るカッコも省略できます(引数が複数、またはゼロの場合には省略できません)。

  1. SampleInterface hoge = str -> {
  2.  return str + str;
  3. };

最後に、処理本文がreturnの1文だけの場合は、{…}とreturnを省略できます。

  1. SampleInterface hoge = str -> str + str;

いかがですか。最初のラムダ式に比べると、ぐんとコードがシンプルになったことが確認できるでしょう。このように、ラムダ式では条件次第でさまざまな記法があるので、理屈も理解しながら、それぞれの記法に慣れていきましょう。

ラムダ式を利用したAPI

ラムダ式の登場以降、コレクションフレームワークを中心に引数としてラムダ式を受け取るメソッドが増えています。たとえば、replaceAllメソッドはリスト内の要素を指定されたルール(ラムダ式)に基づいて置き換えます。

replaceAllメソッド

public default void replaceAll(UnaryOperator<E> operator)
 E:要素の型
 operator:変換ルールを表すラムダ式

たとえば以下は、「あ」で始まる単語の先頭に「*」を付与する例です。

LambdaReplace.java

  1. package com.example.mynavi.object;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.List;
  5.  
  6. public class LambdaReplace {
  7.  public static void main(String[] args) {
  8.   var list = new ArrayList<>(
  9.    List.of("あかさか", "さくらぎちょう", "よよぎ", "あおもり"));
  10.   list.replaceAll(str -> {
  11.    if(str.startsWith("あ")) {
  12.     return "*" + str;
  13.    } else {
  14.     return str;
  15.    }
  16.   });
  17.   System.out.println(list);
  18.   // 結果:[*あかさか, さくらぎちょう, よよぎ, *あおもり]
  19.  }
  20. }

引数operatorは、

  • 引数として個々の要素(str)を受け取り、
  • 戻り値として変換結果を返す

ラムダ式です。引数operatorの型UnaryOperatorは、「引数1個と、同じ型の戻り値を持つ」関数型インターフェイスを表しているわけです。

Javaでは、このようによく利用するラムダ式(関数型インターフェイス)を標準で用意しており、ほとんどの場合、自前で関数型インターフェイスを用意しなくても済むようになっています。以下に、主な関数型インターフェイスをまとめます。

分類 インターフェイス メソッド
値を返さない XxxxxConsumer void accept(xxxxx v)
BiConsumer<T,U> void accept(T t, U u)
値を返す Supplier<T> T get()
Function<T,R> R apply(T t)
BiFunction<T,U,R> R apply(T t, U u)
真偽値を返す Predicate<T> boolean test(T t)
BiPredicate<T,U> boolean test(T t, U u)
演算結果を返す UnaryOperator<T> T apply(T t)
BinaryOperator<T> T apply(T t, T u)
継承、インターフェイス_目次
extendsキーワード
interface命令
implementsキーワード
無名クラスを定義する
ラムダ式を利用する

監修

山田祥寛(有限会社 WINGSプロジェクト)
静岡県榛原町生まれ。一橋大学経済学部卒業後、NEC にてシステム企画業務に携わるが、2003年 4 月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト(https://wings.msn.to/)」の代表でもある。主な著書に『改訂 3 版JavaScript 本格入門』(技術評論社)、「独習シリーズ(C#・Python・PHP・Ruby・JSP&サーブレットなど)」(翔泳社)、「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravel など)」(Amazon Kindle)など。売り上げの累計は100万部を超える。