Backend/JAVA

Functional Interface(ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€)

dddzr 2026. 4. 7. 09:54

πŸ“Œ 1. Functional Interface(ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€)λž€?

  • 단 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ§Œ κ°€μ§€λŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό 말함. (default, static λ©”μ„œλ“œλŠ” μΆ”κ°€λ‘œ μžˆμ–΄λ„ 상관x)
  • 이 μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ λžŒλ‹€μ‹(Lambda Expression)μ΄λ‚˜ λ©”μ„œλ“œ μ°Έμ‘°(Method Reference)둜 κ΅¬ν˜„ κ°€λŠ₯.
  • μžλ°” 8λΆ€ν„° λ„μž…λ¨.

πŸ’‘ 즉, “ν•˜λ‚˜μ˜ κΈ°λŠ₯을 ν‘œν˜„ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€”라고 μƒκ°ν•˜λ©΄ νŽΈν•¨.

 

μ˜ˆμ‹œ

// *@FunctionalInterface = 단 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œλ§Œ 가지도둝 κ°•μ œν•˜λŠ” μ–΄λ…Έν…Œμ΄μ…˜
@FunctionalInterface
public interface MyFunctionalInterface {
    void doSomething(); // 단 ν•˜λ‚˜μ˜ 좔상 λ©”μ„œλ“œ
}

// λžŒλ‹€μ‹μœΌλ‘œ κ΅¬ν˜„ κ°€λŠ₯:
MyFunctionalInterface f = () -> System.out.println("Hello, Functional Interface!");
f.doSomething(); // 좜λ ₯: Hello, Functional Interface!

 


πŸ“Œ 2. μžλ°”μ—μ„œ μ œκ³΅ν•˜λŠ” λŒ€ν‘œ Functional Interface

μΈν„°νŽ˜μ΄μŠ€ λ©”μ„œλ“œ μš©λ„ 예제
Supplier T get() κ°’ 곡급, μ§€μ—° 생성, κΈ°λ³Έκ°’ Supplier<String> s = () -> "Hello"; s.get(); // "Hello"
Consumer void accept(T t) μž…λ ₯κ°’ μ†ŒλΉ„, 처리만 ν•˜κ³  λ°˜ν™˜ μ—†μŒ Consumer<String> c = s -> System.out.println(s); c.accept("Hi");
Function<T,R> R apply(T t) μž…λ ₯κ°’ λ³€ν™˜, λ§€ν•‘, 계산 Function<Integer,String> f = n -> "Number: "+n; f.apply(5); // "Number: 5"
Predicate boolean test(T t) 쑰건 검사, 필터링 Predicate<String> p = s -> s.length()>3; p.test("Hi"); // false

πŸ’‘ μΆ”κ°€ 포인트

  1. Supplier → Stream.generate(), κΈ°λ³Έκ°’, λ‚œμˆ˜ λ“±
  2. Consumer → Stream.forEach(), κ°’ 처리만
  3. Function → Stream.map(), κ°’ λ³€ν™˜
  4. Predicate → Stream.filter(), 쑰건 검사

πŸ“Œ3. μ‚¬μš© 이유

"κ·Έλƒ₯ λ©”μ„œλ“œ 직접 μ“°κΈ°"λž‘ λΉ„κ΅ν–ˆμ„ λ•Œ 뭐가 μ’‹μ„κΉŒ?

 

πŸ‘‰ κ³΅ν†΅μ μœΌλ‘œ: λ‹¨μˆœ 싀행보단 μž¬μ‚¬μš©μ„± + μ‘°ν•©μ„± + 슀트림 API ν™œμš©μ„± λ•Œλ¬Έμ— μœ μš©ν•˜λ‹€!!

  • Consumer → "μ‹€ν–‰ λ™μž‘μ„ λ„˜κ²¨μ€„ 수 μžˆλ‹€"
  • Function → "λ³€ν™˜ λ‘œμ§μ„ λ…λ¦½μ‹œμΌœ μž¬μ‚¬μš©/μ‘°ν•©ν•  수 μžˆλ‹€"
  • Predicate → "쑰건식을 κ°μ²΄ν™”ν•΄μ„œ 필터링과 쑰합에 κ°•λ ₯ν•˜λ‹€"
  • Supplier → "값을 μƒμ„±ν•˜λŠ” μ±…μž„μ„ 뢄리할 수 μžˆλ‹€"

βœ… 3-1. Supplier μ‚¬μš© 이유

Supplier<Double> randomSupplier = () -> Math.random();
System.out.println(randomSupplier.get()); // 0~1 사이 λ‚œμˆ˜

 

⭐ κ·Έλƒ₯ Math.random() ν˜ΈμΆœν•˜λ©΄ λ˜λŠ”λ° μ™œ Supplier?
1️⃣ μ§€μ—° μ‹€ν–‰get() ν˜ΈμΆœν•  λ•Œλ§Œ 싀행됨 (Lazy Evaluation). λΆˆν•„μš”ν•œ μžμ› λ‚­λΉ„λ₯Ό 쀄이고 효율적으둜 μ‹€ν–‰.
2️⃣ μž¬μ‚¬μš©μ„± → κ°’ 생성 λ‘œμ§μ„ λ³€μˆ˜λ‘œ μ €μž₯ν•΄μ„œ μ—¬λŸ¬ κ³³μ—μ„œ ν™œμš© κ°€λŠ₯.
3️⃣ μ „λž΅ ꡐ체 용이 → randomSupplier = () -> new Random().nextDouble();→ κ΅¬ν˜„μ²΄ ꡐ체 κ°€λŠ₯, 호좜 μ½”λ“œλŠ” κ·ΈλŒ€λ‘œ randomSupplier.get() μ‚¬μš©
4️⃣ 슀트림/ν…ŒμŠ€νŠΈμ—μ„œ 유용 → Stream.generate(randomSupplier) κ°€λŠ₯(λ¬΄ν•œμŠ€νŠΈλ¦Ό, .limit(N)으둜 μ œν•œ), ν…ŒμŠ€νŠΈ μ‹œ κ³ μ •κ°’ μ£Όμž… κ°€λŠ₯. 

➑️ 즉, "μ¦‰μ‹œ μ‹€ν–‰"이 μ•„λ‹ˆλΌ "값을 κ³΅κΈ‰ν•˜λŠ” λ™μž‘μ„ κ°μ²΄ν™”ν•΄μ„œ μœ μ—°ν•˜κ²Œ μ“Έ 수 μžˆλ‹€" κ°€ μž₯점.

 

βœ… 3-2. Consumer μ‚¬μš© 이유

Consumer<String> printer = s -> System.out.println("Hello, " + s);
printer.accept("Java"); // Hello, Java

 

⭐ κ·Έλƒ₯ System.out.println("Hello, Java") μ“°λ©΄ λ˜λŠ”λ° μ™œ Consumer?
1️⃣ μž¬μ‚¬μš©μ„± → Consumerλ₯Ό λ³€μˆ˜λ‘œ μ €μž₯ν•΄μ„œ μ—¬λŸ¬ ꡰ데 λ„˜κ²¨ μ“Έ 수 있음.
2️⃣ μ‘°ν•© κ°€λŠ₯andThen 같은 λ©”μ„œλ“œ 체이닝 지원 → μ—¬λŸ¬ Consumerλ₯Ό ν•©μ³μ„œ μ‹€ν–‰ κ°€λŠ₯.
3️⃣ μ „λž΅ ꡐ체 용이 → 같은 λ©”μ„œλ“œ λ‘œμ§μ΄λΌλ„ 상황에 따라 λ‹€λ₯Έ Consumerλ₯Ό μ£Όμž… κ°€λŠ₯ (DI, μ „λž΅ νŒ¨ν„΄μ²˜λŸΌ).

➑️ 즉, "ν•œλ²ˆ μ‹€ν–‰"보닀 "λ™μž‘μ„ λ„˜κΈ°κ³ , μ‘°ν•©ν•˜κ³ , ꡐ체할 수 μžˆλ‹€" κ°€ μž₯점.

 

βœ… 3-3. Function<T, R> μ‚¬μš© 이유

Function<Integer, String> intToStr = i -> "Number: " + i;
System.out.println(intToStr.apply(10)); // Number: 10

 

⭐ κ·Έλƒ₯ ("Number: " + 10) 해도 λ˜λŠ”λ° Function을 μ“°λŠ” 이유?
1️⃣ λ§΅ν•‘ λ‘œμ§μ„ λ³€μˆ˜ν™” → λ³€ν™˜ 과정을 객체처럼 전달할 수 있음.
2️⃣ 슀트림 APIμ—μ„œ κ°•λ ₯map(Function) κ³Ό 같이 데이터 νλ¦„μ—μ„œ λ³€ν™˜μ— ν™œμš©.
3️⃣ ν•¨μˆ˜ ν•©μ„± 지원andThen, compose 둜 μ—¬λŸ¬ Function을 ν•©μ³μ„œ νŒŒμ΄ν”„λΌμΈ 처리 κ°€λŠ₯.

➑️ 즉, "데이터 λ³€ν™˜ λ‘œμ§μ„ λ…λ¦½μ‹œμΌœ μž¬μ‚¬μš© + 슀트림 μ²˜λ¦¬μ— μ΅œμ ν™”".

 

βœ… 3-4. Predicate μ‚¬μš© 이유

Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(4)); // true

 

⭐ κ·Έλƒ₯ if (n % 2 == 0) ν•˜λ©΄ λ˜λŠ”λ° μ™œ Predicate?
1️⃣ 쑰건을 객체처럼 전달 κ°€λŠ₯ → λ©”μ„œλ“œ νŒŒλΌλ―Έν„°λ‘œ "쑰건"을 λ„˜κ²¨μ€„ 수 있음.
2️⃣ 슀트림 ν•„ν„°λ§μ—μ„œ 핡심filter(Predicate) 와 같이 쑰건을 ν™œμš©.
3️⃣ 쑰건 μ‘°ν•© κ°€λŠ₯and, or, negate λ“±μœΌλ‘œ μ—¬λŸ¬ 쑰건을 μœ μ—°ν•˜κ²Œ ν•©μΉ  수 있음.

➑️ 즉, "쑰건을 μ½”λ“œλ‘œ 직접 μ“°λŠ” λŒ€μ‹ , λ…λ¦½λœ 객체둜 λ‹€λ£¨λ©΄μ„œ μ‘°ν•©/전달 κ°€λŠ₯".


πŸ“Œ 4. μ°Έκ³  - Javaμ—μ„œ μ œκ³΅ν•˜λŠ” Functional Interface 전체

  • μžλ°” 8 이후 java.util.function νŒ¨ν‚€μ§€μ— μ•½ 43개 정도가 있음 (2~3μ—μ„œ μ„€λͺ…ν•œ λŒ€ν‘œ4개만 μ•Œμ•„λ‘μž..😒)
  • 크게 μž…λ ₯/좜λ ₯/쑰건/μ‘°ν•©/νŠΉν™”ν˜•μœΌλ‘œ λ‚˜λ‰œλ‹€.
  • *Runnableκ³Ό Callable도 Functional Interface의 일쒅이닀!!

 

1️⃣ μž…λ ₯κ³Ό 좜λ ₯ κΈ°μ€€

νƒ€μž… μ˜ˆμ‹œ
λ§€κ°œλ³€μˆ˜ X, 리턴 O Supplier<T>
λ§€κ°œλ³€μˆ˜ O, 리턴 X Consumer<T>, BiConsumer<T,U>
λ§€κ°œλ³€μˆ˜ O, 리턴 O Function<T,R>, BiFunction<T,U,R>
λ§€κ°œλ³€μˆ˜ O, 리턴 boolean Predicate<T>, BiPredicate<T,U>

 

 

2️⃣ νŠΉν™”ν˜•(Primitive용)

μ„±λŠ₯ μ΅œμ ν™” μœ„ν•΄ int, long, double 버전 제곡

νƒ€μž…  μ˜ˆμ‹œ
int  IntSupplier, IntConsumer, IntFunction<R>, IntPredicate
long  LongSupplier, LongConsumer, LongFunction<R>, LongPredicate
double  DoubleSupplier, DoubleConsumer, DoubleFunction<R>, DoublePredicate



3️⃣ μ‘°ν•©μš©

κΈ°μ‘΄ Functional Interfaceλ₯Ό μ—°κ²°ν•˜κ±°λ‚˜ λ³€ν˜•ν•  λ•Œ μ‚¬μš©

μ˜ˆμ‹œ μ„€λͺ…
UnaryOperator<T> Function<T,T>와 κ°™μŒ, 자기 μžμ‹ μœΌλ‘œ λ§€ν•‘
BinaryOperator<T> BiFunction<T,T,T>와 κ°™μŒ, 두 κ°’μœΌλ‘œ κ²°κ³Ό 생성