Java 함수형 인터페이스(Function, Predicate, Consumer 등) 완벽 이해하기
Java 8 이후 Java 스타일을 완전히 바꾼 핵심 기능 중 하나가 바로:
함수형 인터페이스(Functional Interface)
입니다.
특히 현대 Java에서는:
- Stream API
- Lambda
- CompletableFuture
- Optional
- 비동기 처리
전부 함수형 인터페이스 기반으로 동작합니다.
예:
list.stream()
.filter(x -> x > 10)
여기서:
x -> x > 10
이 람다가 실제로는:
Predicate 함수형 인터페이스 구현
입니다.
즉 람다는 혼자 존재하는 게 아니라:
함수형 인터페이스와 세트
입니다.
이번 글에서는:
- 함수형 인터페이스란?
- 왜 필요한가
- Function
- Predicate
- Consumer
- Supplier
- Unary/BinaryOperator
- Stream API 연결
- 실무 사용 패턴
까지 깊게 정리해보겠습니다.
1. 함수형 인터페이스란?
Functional interface 는:
추상 메서드가 하나만 존재하는 인터페이스
입니다.
2. 왜 중요할까?
람다 표현식은 반드시:
함수형 인터페이스
에 연결되어야 하기 때문.
3. 예시
@FunctionalInterface
interface MyFunction {
void run();
}
4. 사용 예시
MyFunction f =
() -> System.out.println("Hello");
5. 왜 메서드 하나만 가능할까?
람다는:
“어떤 메서드를 구현하는지”
명확해야 하기 때문.
6. @FunctionalInterface란?
@FunctionalInterface
는:
함수형 인터페이스 검증용 어노테이션
입니다.
7. 장점
컴파일러가:
추상 메서드 2개 이상이면 오류
검출 가능.
8. Java가 기본 제공하는 함수형 인터페이스들
매우 중요합니다.
대표 패키지:
java.util.function
9. 가장 핵심 4개
| 인터페이스 | 의미 |
| Function | 입력 → 출력 |
| Predicate | 조건 검사(boolean) |
| Consumer | 입력 소비 |
| Supplier | 값 공급 |
10. Function<T,R>
의미 : T 입력타입, R 반환타입
11. 예시
Function<String, Integer> f =
s -> s.length();
12. 의미
String 입력
↓
길이(Integer) 반환
13. Function<? super T, ? extends R>
14. apply() 메서드
핵심 메서드:
f.apply("Java");
↓
4
15. Function은 어디서 많이 쓰일까?
대표:
- map()
- 변환 로직
- DTO 변환
- Stream API
16. Stream map() 내부
list.stream()
.map(x -> x * 2)
여기 람다는:
Function<T,R>
입니다.
Stream.map(...)
↓
<R> Stream<R> map(
Function<? super T, ? extends R>
)
17. Predicate<T> ⭐⭐⭐⭐⭐
의미:
T를 받아서 boolean을반환하는 함수형 인터페이스
실제 정의:
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
예시:
Predicate<Integer> p =
x -> x > 10;
18. test() 메서드
p.test(20);
↓
true
19. Predicate는 어디서 많이 쓸까?
대표:
- filter()
- validation
- 조건 검사
20. Stream.filter() 시그니처
실제로
Stream<T> filter(
Predicate<? super T> predicate
)
의미:
Stream<T> : 반환타입
filter : 메서드명
Predicate<? super T> predicate : 인자값 (타입 T를 받는 Predicate 타입의 변수
Stream<String>은 Predicate<String>를 넘김.
filter는 왜 Predicate를 받을까?
"조건식을 평가해서 true/false를 반환해야 하기 때문"
21. Stream filter()
list.stream()
.filter(x -> x > 10)
여기 람다는:
Predicate<T>
입니다. 인자를 받고 참거짓을 반환
Stream filter 내부
3글자 이상만 필터링
names.stream()
.filter(name -> name.length() >= 3)
.forEach(System.out::println);
여기서
name -> name.length() >= 3
는 사실
Predicate<String>
이다.
Predicate<Integer> p = x -> x > 10;
22. Function과 Predicate 차이
Function
Function<String, Integer>
String
↓
Integer
데이터 변환
예시
.map(String::length)
Predicate
Predicate<String>
String
↓
boolean
조건 검사
예시
.filter(str -> str.length() > 3)
23. Consumer<T>
의미 :
Consumer<T>
↓
입력 소비
반환 없음(void)
24. 예시
Consumer<String> c =
s -> System.out.println(s);
25. accept() 메서드
c.accept("Java");
↓
출력 수행
26. Consumer 사용처
대표:
- forEach()
- 로그 출력
- 이벤트 처리
27. Stream forEach()
list.forEach(
x -> System.out.println(x)
);
여기 람다는:
Consumer<T>
입니다.
28. Supplier<T>
반대로 입력 없이 값 생성.
의미 :
Supplier<T>
↓
출력만 존재
29. 예시
Supplier<Double> s =
() -> Math.random();
30. get() 메서드
s.get();
↓
랜덤 값 반환
31. Supplier 사용처
대표:
- 객체 생성
- lazy loading
- Optional.orElseGet()
32. UnaryOperator<T>
특수 Function.
33. 의미
입력 타입 = 반환 타입
34. 예시
UnaryOperator<Integer> op =
x -> x * 2;
35. BinaryOperator<T>
동일 타입 두 개 입력.
36. 예시
BinaryOperator<Integer> op =
(a, b) -> a + b;
37. Predicate 조합 가능
매우 중요.
p1.and(p2)
p1.or(p2)
p1.negate()
지원.
38. Function 조합 가능
f1.andThen(f2)
f1.compose(f2)
39. 실무에서 기억하는 방법
| 인터페이스 | 의미 |
| Function<T,R> | T → R 변환 |
| Predicate<T> | T → boolean 검사 |
| Consumer<T> | T 소비(return 없음) |
| Supplier<T> | T 공급(파라미터 없음) |
예시
Function<User, String>
↓
User → 이름
Predicate<User>
↓
User → 조건 만족 여부
Consumer<User>
↓
User 받아서 처리
Supplier<User>
↓
User 생성
40. 그래서 Stream API를 이해할 때는 이렇게 연결돼.
stream
.filter(...)
.map(...)
.forEach(...)
↓
filter → Predicate<T>
map → Function<T,R>
forEach → Consumer<T>
이 3개가 Stream에서 가장 핵심적인 함수형 인터페이스야.
f1.andThen(f2)
f1.compose(f2)
41. 함수형 프로그래밍 스타일
핵심 철학:
동작을 값처럼 전달
입니다.
42. Stream API가 가능한 이유
람다 + 함수형 인터페이스 조합 때문.
43. Optional과도 연결
예:
optional.orElseGet(
() -> createDefault()
);
여기:
Supplier 사용
44. CompletableFuture도 동일
future.thenApply(
x -> x * 2
);
여기:
Function 사용
45. 메서드 레퍼런스와 연결
예:
System.out::println
실제로는:
Consumer 구현
입니다.
46. 실무에서 자주 하는 실수
1) 함수형 인터페이스 없이 람다 사용 가능하다고 생각
불가능.
2) Predicate와 Function 혼동
boolean 반환 여부 중요.
3) Consumer인데 return 하려 함
void 인터페이스임.
4) 람다 안에 복잡 로직 작성
가독성 저하.
47. 핵심 비교 정리
| 인터페이스 | 입력 | 반환 |
| Function<T,R> | O | O |
| Predicate<T> | O | boolean |
| Consumer<T> | O | void |
| Supplier<T> | X | O |
48. 핵심 흐름 요약
람다 표현식
↓
함수형 인터페이스 구현
↓
Stream/API 전달
↓
동작 수행
49. 가장 중요한 핵심 한 줄
함수형 인터페이스는
“람다를 담는 Java의 타입 시스템”이다
입니다.
50. 정리
함수형 인터페이스는 단순 문법 요소가 아닙니다.
실제로는:
- Lambda
- Stream API
- Optional
- CompletableFuture
- 함수형 프로그래밍
전체와 연결되는 Java 8 이후 핵심 설계 기반입니다.
특히 실무에서는:
- Function
- Predicate
- Consumer
- Supplier
- Stream 내부 동작
을 정확히 이해하는 것이 매우 중요합니다.
다음 글에서는:
Stream API 기초
를 내부 반복, lazy evaluation, pipeline 구조, map/filter/reduce 원리까지 포함해서 깊게 정리해보겠습니다.
'language > java' 카테고리의 다른 글
| Java Method Reference(::) 완벽 이해하기 (0) | 2026.05.28 |
|---|---|
| Java Stream API 기초 완벽 이해하기 (0) | 2026.05.27 |
| Java 익명 클래스 vs 람다 완벽 이해하기 (1) | 2026.05.27 |
| Java 람다 표현식(Lambda) 완벽 이해하기 (0) | 2026.05.27 |
| Java ConcurrentHashMap 기초 완벽 이해하기 (0) | 2026.05.27 |
댓글