はじめに
こんにちは。
スッキリわかるJava入門 実践編の学習を進めています。
この学習記録を備忘録として残しながら、自身の理解を深めたいと思っています。
今回は第1部 / 第3章「関数とラムダ式」について学びました。
関数をオブジェクト化し、「ラムダ式」や「Stream API」で処理することで、プログラムの意図が明確に表現でき、コードがシンプルになります。
この記事が、同じくJavaの学習をしている人にとっても参考になりましたら幸いです。
参考書籍について
- 書籍名:スッキリわかるJava入門 実践編
- 著者:中山清喬
- 出版社:インプレス
目次
1. 本章の要点
関数オブジェクト
- Javaでは関数を第1級オブジェクトとして扱え、変数に代入したり、メソッドの引数として渡せます。
- 1つの抽象メソッドのみを持つインタフェース(関数インタフェース)を使うことで、関数をオブジェクトとして格納できます。
// 関数インターフェース:1つの抽象メソッドのみを持つ
@FunctionalInterface
interface Calculate {
int execute(int a, int b);
}
// 関数オブジェクトを変数に代入
Calculate add = (x, y) -> x + y;
Calculate multiply = (x, y) -> x * y;
public class Main {
public static void main(String[] args) {
System.out.println(add.execute(10, 5)); // 出力:15
System.out.println(multiply.execute(10, 5)); // 出力:50
}
}
java.util.functionパッケージの関数インターフェース
import java.util.function.*;
public class Main {
public static void main(String[] args) {
// Function<入力型, 出力型>:入力を受け取り、出力を返す
Function<Integer, String> intToString = (n) -> "数字:" + n;
System.out.println(intToString.apply(42)); // 出力:数字:42
// Predicate<入力型>:入力を受け取り、真偽値を返す
Predicate<Integer> isPositive = (n) -> n > 0;
System.out.println(isPositive.test(10)); // 出力:true
System.out.println(isPositive.test(-5)); // 出力:false
// Consumer<入力型>:入力を受け取り、処理するが値を返さない
Consumer<String> print = (s) -> System.out.println(s);
print.accept("こんにちは"); // 出力:こんにちは
}
}
javaの世界における関数
何らかの情報(Input)を引数として受け取り、
何らかの処理(Process)を行い、
何らかの結果(Output)を戻り値として返す、ひとかたまりの処理ロジックです。
ラムダ式
- ラムダ式とは、名前のないメソッド(無名関数)をシンプルに記述するための文法です。
- ラムダ式を使うことで、関数の実体をプログラム実行中に簡潔に生成できます。
- さまざまな省略記法が用意されており、シンプルな関数なら最小限のコードで記述できます。
// 従来の匿名クラス(冗長)
Function<Integer, Integer> doubleOld = new Function<Integer, Integer>() {
@Override
public Integer apply(Integer x) {
return x * 2;
}
};
// ラムダ式(簡潔)
Function<Integer, Integer> double1 = (x) -> x * 2;
// さらに省略可能
Function<Integer, Integer> double2 = x -> x * 2;
// メソッド参照で最も簡潔に
class Calculator {
static int doubleIt(int x) { return x * 2; }
}
Function<Integer, Integer> double3 = Calculator::doubleIt;
public class Main {
public static void main(String[] args) {
System.out.println(double1.apply(10)); // 出力:20
System.out.println(double2.apply(10)); // 出力:20
System.out.println(double3.apply(10)); // 出力:20
}
}
Stream API
- Stream APIとは、高階関数を活用してコレクションや配列などのデータを処理するためのAPIです。
- コレクションや配列から生成したストリームに対してメソッドを呼ぶことで、
宣言的なデータ処理が実現できます。
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 従来のループ処理(命令的)
List<Integer> evenNumbers1 = new ArrayList<>();
for (Integer n : numbers) {
if (n % 2 == 0) {
evenNumbers1.add(n * n);
}
}
System.out.println(evenNumbers1); // 出力:[4, 16, 36, 64, 100]
// Stream APIで宣言的に記述
List<Integer> evenNumbers2 = numbers.stream()
.filter(n -> n % 2 == 0) // 中間処理:偶数を絞り込む
.map(n -> n * n) // 中間処理:2乗に変換
.collect(Collectors.toList()); // 終端処理:Listに変換
System.out.println(evenNumbers2); // 出力:[4, 16, 36, 64, 100]
}
}
高階関数
引数として関数を受け取る関数を高階関数と呼びます。
filter()やmap()などのメソッドを組み合わせることで、「何をしたいか」に焦点を当てた宣言的な記述ができます。
2. 自分なりの解釈
- 関数をオブジェクトとして扱えることで、処理ロジック自体を値として変数に格納でき、より柔軟なプログラム設計ができるようになった。
- ラムダ式によって、匿名クラスの冗長な記述が不要になり、処理の本質に集中できるようになった。
- Stream APIを使うことで、「どうやってループするか」という詳細を意識せず、「何をしたいか」という意図が明確に表現されるようになった。
最後まで見ていただきありがとうございました。
次回は、第1部 / 第4章「JVM制御とリフレクション」について書く予定です。