Contents
see ListJava 함수형 프로그래밍 - Lambda와 Method Reference
Java 8부터 도입된 람다 표현식과 메서드 참조를 사용하면 간결하고 읽기 쉬운 코드를 작성할 수 있습니다.
언제 사용하나요?
- 컬렉션 필터링, 매핑, 정렬
- 이벤트 핸들러, 콜백 구현
- 스트림 API와 함께 사용
- 함수를 파라미터로 전달할 때
람다 표현식 기본 문법
// 기본 형태
(파라미터) -> { 실행문; }
// 파라미터 1개면 괄호 생략 가능
x -> x * 2
// 실행문 1개면 중괄호, return 생략 가능
(a, b) -> a + b
// 타입 추론 가능하면 타입 생략
(String s) -> s.length() // 명시
s -> s.length() // 생략Before / After 비교
// Before - 익명 클래스
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
// After - 람다
Collections.sort(list, (a, b) -> a.compareTo(b));
// 더 간단하게 - 메서드 참조
Collections.sort(list, String::compareTo);함수형 인터페이스
// 하나의 추상 메서드를 가진 인터페이스
@FunctionalInterface
public interface Calculator {
int calculate(int a, int b);
}
// 람다로 구현
Calculator add = (a, b) -> a + b;
Calculator multiply = (a, b) -> a * b;
System.out.println(add.calculate(3, 5)); // 8
System.out.println(multiply.calculate(3, 5)); // 15주요 내장 함수형 인터페이스
| 인터페이스 | 메서드 | 용도 |
|---|---|---|
| Predicate<T> | boolean test(T t) | 조건 검사 |
| Function<T,R> | R apply(T t) | 변환 |
| Consumer<T> | void accept(T t) | 소비 (반환값 없음) |
| Supplier<T> | T get() | 생성 (입력 없음) |
| BiFunction<T,U,R> | R apply(T t, U u) | 2개 입력 변환 |
Predicate - 조건 검사
Predicate<String> isEmpty = s -> s.isEmpty();
Predicate<String> isLong = s -> s.length() > 10;
// 사용
isEmpty.test(""); // true
isLong.test("Hello World"); // true
// 조합
Predicate<String> isNotEmpty = isEmpty.negate();
Predicate<String> combined = isEmpty.or(isLong);
// 스트림에서
list.stream()
.filter(s -> s.length() > 5)
.collect(Collectors.toList());Function - 변환
Function<String, Integer> toLength = s -> s.length();
Function<Integer, Integer> square = n -> n * n;
// 사용
toLength.apply("Hello"); // 5
// 합성
Function<String, Integer> composed = toLength.andThen(square);
composed.apply("Hello"); // 25 (5의 제곱)
// 스트림에서
list.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList());Consumer - 소비
Consumer<String> printer = s -> System.out.println(s);
Consumer<String> logger = s -> log.info(s);
// 사용
printer.accept("Hello");
// 체이닝
Consumer<String> both = printer.andThen(logger);
// 스트림에서
list.forEach(s -> System.out.println(s));Supplier - 생성
Supplier<LocalDateTime> now = () -> LocalDateTime.now();
Supplier<Double> random = () -> Math.random();
Supplier<User> userFactory = () -> new User();
// 사용
LocalDateTime time = now.get();
// 지연 실행에 유용
Optional.ofNullable(value)
.orElseGet(() -> createDefault());메서드 참조 (Method Reference)
// 4가지 유형
// 1. 정적 메서드 참조
Function<String, Integer> parser = Integer::parseInt;
// = s -> Integer.parseInt(s)
// 2. 인스턴스 메서드 참조 (특정 객체)
String str = "Hello";
Supplier<Integer> lengthSupplier = str::length;
// = () -> str.length()
// 3. 인스턴스 메서드 참조 (임의 객체)
Function<String, Integer> length = String::length;
// = s -> s.length()
// 4. 생성자 참조
Supplier<List<String>> listFactory = ArrayList::new;
// = () -> new ArrayList<>()
Function<String, User> userFactory = User::new;
// = name -> new User(name)스트림과 함께 사용
List<User> users = Arrays.asList(
new User("Kim", 30),
new User("Lee", 25),
new User("Park", 35)
);
// 필터링 + 매핑 + 수집
List<String> names = users.stream()
.filter(u -> u.getAge() >= 30) // Predicate
.map(User::getName) // Function
.sorted() // Comparator
.collect(Collectors.toList());
// 출력
users.forEach(System.out::println); // Consumer
// 조건 검사
boolean allAdult = users.stream()
.allMatch(u -> u.getAge() >= 18); // Predicate실전 활용
// 정렬
users.sort(Comparator.comparing(User::getAge));
users.sort(Comparator.comparing(User::getName).reversed());
// Optional과 함께
Optional.ofNullable(user)
.map(User::getName)
.filter(name -> !name.isEmpty())
.orElse("Unknown");
// 그룹핑
Map<Integer, List<User>> byAge = users.stream()
.collect(Collectors.groupingBy(User::getAge));