こんにちは!バックエンドエンジニアのキョンです。今日は、実務で本当によく使うJavaのLambda式について、5年間の開発経験を元に詳しく解説していきます。
目次
1. Lambda式の基礎知識
Lambda式って結局なに?
最初の頃、私もLambda式を見るたびに「なんだこの矢印は?」って思ってました(笑) 簡単に言うと、Lambda式は「その場で作る使い捨ての関数」みたいなものです。
基本構文
// 基本形
(引数) -> { 処理 }
// 例:整数を2倍にする関数
Function<Integer, Integer> doubled = x -> x * 2;
// 複数行の処理
BiFunction<Integer, Integer, Integer> sum = (a, b) -> {
System.out.println("計算します");
return a + b;
};
2. よく使うLambda式のパターン
コレクションの処理
List<User> users = getUserList();
// フィルタリング
users.stream()
.filter(user -> user.getAge() >= 20)
.collect(Collectors.toList());
// マッピング
List<String> userNames = users.stream()
.map(user -> user.getName())
.collect(Collectors.toList());
イベントハンドリング
特にSpringBootでよく使います:
@Component
public class UserEventHandler {
private final Consumer<User> notifyAdmin = user -> {
String message = String.format("新規ユーザー登録: %s", user.getName());
adminNotificationService.send(message);
};
public void handleNewUser(User user) {
notifyAdmin.accept(user);
}
}
3. 実践的なユースケース
ケース1: バリデーション処理
実際のプロジェクトで使用している例です:
public class ValidationUtils {
private static final Predicate<String> isValidEmail = email ->
email != null && email.matches("^[A-Za-z0-9+_.-]+@(.+)$");
private static final Predicate<String> isValidPassword = password ->
password != null && password.length() >= 8;
public boolean validateUser(User user) {
return isValidEmail.test(user.getEmail()) &&
isValidPassword.test(user.getPassword());
}
}
ケース2: データ変換処理
API開発でよく使うパターンです:
public class UserConverter {
private final Function<User, UserDTO> toDTO = user -> new UserDTO(
user.getId(),
user.getName(),
user.getEmail(),
user.getCreatedAt()
);
private final Function<List<User>, List<UserDTO>> toDTOList =
users -> users.stream()
.map(toDTO)
.collect(Collectors.toList());
public List<UserDTO> convertUsers(List<User> users) {
return toDTOList.apply(users);
}
}
ケース3: 条件分岐の整理
複雑な条件分岐をスッキリさせる例:
public class OrderProcessor {
private final Map<String, Consumer<Order>> orderHandlers = new HashMap<>();
public OrderProcessor() {
orderHandlers.put("PENDING", this::processPendingOrder);
orderHandlers.put("CONFIRMED", this::processConfirmedOrder);
orderHandlers.put("SHIPPED", this::processShippedOrder);
}
public void processOrder(Order order) {
orderHandlers.getOrDefault(order.getStatus(),
(o) -> log.warn("Unknown status: {}", o.getStatus()))
.accept(order);
}
}
4. 注意点とベストプラクティス
可読性を意識した書き方
// 悪い例:ネストが深い
users.stream()
.filter(u -> u.getOrders().stream()
.anyMatch(o -> o.getTotal() > 10000))
.collect(Collectors.toList());
// 良い例:述語を分離
Predicate<User> hasLargeOrder = user ->
user.getOrders().stream()
.anyMatch(order -> order.getTotal() > 10000);
users.stream()
.filter(hasLargeOrder)
.collect(Collectors.toList());
例外処理
public class ExceptionHandler {
private final Function<String, Integer> safeParser = str -> {
try {
return Integer.parseInt(str);
} catch (NumberFormatException e) {
log.error("数値パースエラー: {}", str);
return 0;
}
};
}
5. パフォーマンスの考慮点
メモリ使用量の最適化
// 悪い例:中間オブジェクトが多い
List<String> names = users.stream()
.map(User::getName)
.collect(Collectors.toList());
names.forEach(this::process);
// 良い例:直接処理
users.stream()
.map(User::getName)
.forEach(this::process);
キャプチャリングの注意点
public class PerformanceExample {
private int counter = 0;
// 悪い例:状態を変更するLambda
public void badCount() {
IntConsumer counter = i -> this.counter += i;
}
// 良い例:関数型スタイルを維持
public int goodCount(int value) {
return counter + value;
}
}
まとめ
Lambda式は、最初は見慣れない構文に戸惑うかもしれませんが、使いこなせるようになると本当に便利なツールです。
個人的なおすすめポイントは:
- まずは簡単なStream操作から始める
- 徐々に複雑な処理に挑戦する
- 可読性を意識して書く
- パフォーマンスを考慮する
さらなる学習のために
- Java 8のドキュメント
- Effective Java 第3版
- Modern Java in Action
次回は「Java Optional クラスの実践的な使い方」について解説する予定です。お楽しみに!
コメント