본문 바로가기
language/java

Java 함수형 인터페이스(Function, Predicate, Consumer 등) 완벽 이해하기

by 죄니안죄니 2026. 5. 27.
반응형

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>

의미: 입력은 super, 반환은 extends
T 혹은 T의 부모 , R또는 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 원리까지 포함해서 깊게 정리해보겠습니다.

반응형

댓글