Java Lambda 式

Java 8 新特性Java 8 の新機能


Lambda 式は、クロージャとも呼ばれ、Java 8 のリリースを後押しした最も重要な新機能です。

Lambda では、関数をメソッドの引数として渡すことができます(関数をパラメータとしてメソッドに渡します)。

Lambda 式を使用すると、コードをより簡潔でコンパクトにできます。

構文

lambda 式の構文形式は次のとおりです。

(parameters) -> expression 或 (parameters) ->{ statements; }

parametersはパラメータリストです。expression 或 { statements; }は Lambda 式の本体です。パラメータが1つのみの場合は括弧を省略できます。パラメータがない場合は、空の括弧が必要です。

次は、Lambda 式を使用して2つの数値の合計を計算する簡単な例です。

例

// Lambda 式を使用して2つの数値の合計を計算する
MathOperation addition = (a, b) -> a + b;

// Lambda 式を呼び出す
int result = addition.operation(5, 3);
System.out.println("5 + 3 = " + result);

上記の例では、MathOperation は関数型インターフェースであり、抽象メソッド operation を含んでいます。Lambda 式は(a, b) -> a + bこの抽象メソッドを実装し、2つの引数を加算する操作を表します。

Lambda 式は、コレクション操作やイベント処理など、さまざまなシナリオで使用できます。次は、Lambda 式を使用してリストを走査する例です。

例

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// Lambda 式を使用してリストを走査する
names.forEach(name -> System.out.println(name));

Lambda 式は Java により関数型プログラミングのスタイルをもたらし、コードをより簡潔で読みやすくします。これは Java 8 における関数型プログラミングへの重要な改良です。


重要な特徴

簡潔さ

Lambda 式は、特に関数型インターフェースに適した、より簡潔な構文を提供します。従来の匿名内部クラスと比較して、Lambda 式はコードをよりコンパクトにし、ボイラープレートコードの記述を減らします。

例

// 従来の匿名内部クラス
Runnable runnable1 = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello World!");
    }
};

// Lambda 式を使用する
Runnable runnable2 = () -> System.out.println("Hello World!");

関数型プログラミングのサポート

Lambda 式は関数型プログラミングの一つの現れであり、関数をメソッドの引数として渡したり、関数を戻り値として返したりすることを可能にします。このサポートにより、Java は関数型プログラミングにおいてより柔軟になり、コレクション操作や並列計算などのタスクをより適切に処理できます。

例

// Lambda 式をメソッドの引数として渡す
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));

変数のキャプチャ

Lambda 式は外部スコープの変数にアクセスできます。この特性を変数のキャプチャと呼びます。Lambda 式は、final または実質的に final であるローカル変数を暗黙的にキャプチャできます。

例

// 変数のキャプチャ
int x = 10;
MyFunction myFunction = y -> System.out.println(x + y);
myFunction.doSomething(5); // 15 を出力

メソッド参照

Lambda 式はメソッド参照によってさらに簡略化できます。メソッド参照を使用すると、冗長なコードを書くことなく、既存のクラスやオブジェクトのメソッドを直接参照できます。

例

// メソッド参照を使用する
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);

並列性

Lambda 式を使用すると、並列操作をより簡単に実装できます。Stream API と Lambda 式を組み合わせることで、並列計算を容易に実装し、プログラムのパフォーマンスを向上させることができます。

例

// Lambda 式と Stream API を使用して並列計算を行う
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();

Lambda 式の導入により、Java プログラミングはより柔軟で簡潔になり、関数型プログラミングの発展を促進しました。


Lambda 式の例

Lambda 式の簡単な例:

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

Java8Tester.java ファイルに次のコードを入力します:

Java8Tester.java ファイル

public class Java8Tester { public static void main(String args[]){ Java8Tester tester = new Java8Tester(); //型宣言 MathOperation addition = (int a, int b) -> a + b; //型宣言なし MathOperation subtraction = (a, b) -> a - b; //中括弧内の return 文 MathOperation multiplication = (int a, int b) -> { return a * b; }; //中括弧と return 文なし MathOperation division = (int a, int b) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); //括弧なし GreetingService greetService1 = message -> System.out.println("Hello " + message); //括弧あり GreetingService greetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage("サンプル"); greetService2.sayMessage("Google"); } interface MathOperation { int operation(int a, int b); } interface GreetingService { void sayMessage(String message); } private int operate(int a, int b, MathOperation mathOperation){ return mathOperation.operation(a, b); } }

上記のスクリプトを実行すると、出力結果は次のとおりです:

$ javac Java8Tester.java 
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello サンプル
Hello Google

Lambda 式を使用する際は、次の2点に注意する必要があります:

  • Lambda 式は主に、インラインで実行されるメソッド型インターフェース(例: 単純なメソッドインターフェース)を定義するために使用されます。上記の例では、さまざまなタイプの Lambda 式を使用して MathOperation インターフェースのメソッドを定義し、その後 operation の実行を定義しました。

  • Lambda 式は匿名メソッドを使用する手間を省き、Java にシンプルかつ強力な関数型プログラミング能力をもたらします。


変数のスコープ

lambda 式は、final と宣言された外部ローカル変数のみを参照できます。つまり、lambda 内でスコープ外で定義されたローカル変数を変更することはできません。変更するとコンパイルエラーになります。

Java8Tester.java ファイルに次のコードを入力します:

Java8Tester.java ファイル

public class Java8Tester { final static String salutation = "Hello! "; public static void main(String args[]){ GreetingService greetService1 = message -> System.out.println(salutation + message); greetService1.sayMessage("サンプル"); } interface GreetingService { void sayMessage(String message); } }

上記のスクリプトを実行すると、出力結果は次のとおりです:

$ javac Java8Tester.java 
$ java Java8Tester
Hello! サンプル

lambda 式内で外部のローカル変数に直接アクセスすることもできます:

Java8Tester.java ファイル

public class Java8Tester { public static void main(String args[]) { final int num = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); //出力結果は 3 です } public interface Converter<T1, T2> { void convert(int i); } }

lambda 式のローカル変数は final として宣言する必要はありませんが、後続のコードで変更されてはなりません(つまり、暗黙的に final のセマンティクスを持ちます)。

int num = 1;  
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5;  
//报错信息:Local variable num defined in an enclosing scope must be final or effectively 
 final

Lambda 式内では、ローカル変数と同じ名前のパラメータまたはローカル変数を宣言することはできません。

String first = "";  
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length());  //编译会出错 

Java 8 新特性Java 8 の新機能

その他の拡張