Post

Java `java.util.function` 핵심 정리

Java `java.util.function` 핵심 정리

Java java.util.function 핵심 정리

람다를 다형성 있게 쓰기 위한 표준 함수형 인터페이스 모음집 Java 8부터 도입되어 Stream, Optional, Map 등 현대적인 Java API의 기반이 된다.


java.util.function이 필요한가?

Java에서 람다는 단독으로 존재할 수 없다. 반드시 함수형 인터페이스(추상 메서드 1개) 를 타겟 타입으로 가져야 한다.

1
x -> x * 2   // 타입 없음 (단독 사용 불가)
1
Function<Integer, Integer> f = x -> x * 2; // 정상

➡️ java.util.function람다의 표준 타겟 타입을 제공하기 위해 만들어졌다.


Java 8 이전에도 존재했던 Functional Interface

람다가 Java 8에서 추가되었지만, 함수형 인터페이스 자체는 그 이전부터 존재했다. 대표적인 예가 바로 Runnable, Comparator 등이다.

Runnable

1
Runnable r = () -> System.out.println("run");
1
2
3
public interface Runnable {
    void run();
}
  • 추상 메서드가 1개 → 함수형 인터페이스
  • Java 1.0부터 존재
  • Java 8에서 람다의 대표적인 타겟 타입이 됨

Comparator

1
Comparator<Integer> comp = (a, b) -> a - b;
1
2
3
public interface Comparator<T> {
    int compare(T o1, T o2);
}
  • 두 값을 비교하는 함수형 인터페이스
  • 정렬, 우선순위 로직에 핵심적으로 사용
1
list.sort((a, b) -> b - a);

핵심 포인트

람다는 새로운 개념이 아니라, 기존 인터페이스 구현 문법을 간결하게 만든 것

  • Java 8 이전: 익명 클래스
  • Java 8 이후: 람다 표현식
1
2
3
4
5
6
7
// Java 7
new Runnable() {
    @Override
    public void run() {
        System.out.println("run");
    }
};
1
2
// Java 8+
() -> System.out.println("run");

만약 표준 Functional Interface가 없었다면?

java.util.function이 없다면, API마다 제각각의 인터페이스를 정의해야 했을 것이다.

1
2
3
interface MyMapper<T, R> { R map(T t); }
interface MyFilter<T> { boolean check(T t); }
interface MyCreator<T> { T create(); }
  • API 간 람다 호환 불가
  • 타입 불일치 빈번
  • 다형성 활용 불가

➡️ 결과적으로 람다는 문법 설탕에 그치고 생태계는 붕괴


가장 중요한 4대장 (이것만 알아도 80%)

1️⃣ Function<T, R> — 변환

1
R apply(T t)

입력값을 받아 다른 타입으로 변환한다.

1
Function<String, Integer> length = String::length;

사용처

  • Stream.map()
  • Optional.map()
  • Map.computeIfAbsent()

2️⃣ Predicate — 조건

1
boolean test(T t)

참/거짓을 판단하는 조건 함수

1
Predicate<Integer> isPositive = n -> n > 0;

조합 가능

1
p.and(p2).or(p3).negate();

사용처

  • Stream.filter()

3️⃣ Consumer — 소비

1
void accept(T t)

값을 받아서 사용만 하고 반환값 없음

1
Consumer<String> printer = System.out::println;

사용처

  • Stream.forEach()
  • Map.forEach()

4️⃣ Supplier — 공급

1
T get()

입력 없이 값을 생성해서 제공

1
Supplier<UUID> uuidSupplier = UUID::randomUUID;

⚠️ 지연 실행(lazy) 이 핵심

1
optional.orElseGet(() -> createExpensiveObject());

입력이 2개인 Bi 계열

인터페이스메서드용도
BiFunction<T, U, R>apply(T, U)두 값 계산
BiPredicate<T, U>test(T, U)두 값 비교
BiConsumer<T, U>accept(T, U)Map 처리
1
BiFunction<Integer, Integer, Integer> sum = Integer::sum;
1
map.forEach((k, v) -> System.out.println(k + ":" + v));

Unary / Binary Operator

자기 자신 타입을 다루는 특수한 Function

인터페이스의미
UnaryOperatorT → T
BinaryOperator(T, T) → T
1
2
UnaryOperator<Integer> square = n -> n * n;
BinaryOperator<Integer> max = Integer::max;

기본 타입 특화 (성능 중요)

오토박싱 제거 목적

자주 쓰는 것만 정리

인터페이스설명
IntPredicateint 조건
IntConsumerint 소비
IntSupplierint 공급
ToIntFunctionT → int
1
IntPredicate isEven = n -> n % 2 == 0;

주요 API와 매핑 관계

Java API요구 인터페이스
Stream.mapFunction
Stream.filterPredicate
Stream.forEachConsumer
Optional.mapFunction
Optional.orElseGetSupplier
Map.forEachBiConsumer
Map.computeIfAbsentFunction

언제 직접 함수형 인터페이스를 만들까?

❌ 이런 경우 → 기본 제공 사용

1
2
Function<User, String>
Predicate<Order>

⭕ 이런 경우 → 직접 정의

1
2
3
4
@FunctionalInterface
interface PasswordPolicy {
    boolean validate(String password);
}

➡️ 도메인 의미가 중요할 때


한 줄 요약 (암기용)

1
2
3
4
변환 → Function
조건 → Predicate
소비 → Consumer
공급 → Supplier

결론

java.util.function은 람다를 다형성 있게 쓰기 위한 표준 인터페이스 세트이며, 위 4대장만 이해해도 Java 함수형 프로그래밍의 대부분을 커버할 수 있다.

This post is licensed under CC BY 4.0 by the author.