
* 개념 복습과 학습 정도를 파악하고자 포스팅합니다!
* Claude, ChatGPT를 활용하여 이미지를 생성하고 활용합니다.
람다 표현식과 함수형 인터페이스
1. 람다가 왜 나왔을까?
Java 8 이전엔 함수를 값처럼 전달하려면 익명 클래스를 사용해야 했어요. 이로 인해 코드가 장황하고 읽기 힘들다는 단점이 있었죠!
List<String> names = Arrays.asList("Charlie", "Alice", "Bob");
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.compareTo(b);
}
});
람다가 도입되면서 아래 코드처럼 바뀝니다.
Collections.sort(names, (a, b) -> a.compareTo(b));
// 더 줄이면
names.sort(String::compareTo);
같은 동작을 하는 코드가 7줄에서 1줄로 줄었죠?
람다의 핵심은 함수를 값처럼 다루는 것입니다.
2. 람다 문법 해부하기
(a, b) -> { 본 문 }
매개변수 화살표 실행 코드
파라미터 없음
() -> System.out.println("Hello")
파라미터 1개
x -> x * x
파라미터 1개 (괄호 생략)
name -> "Hello, " + name
파라미터 2개
(a, b) -> a + b
본문 여러 줄
(a, b) -> { int sum = a + b; return sum; }
타입 명시
(String s, int n) -> s.repeat(n)
생략 규칙
// 타입 생략 가능
(String a, String b) -> a + b // 생략 전
(a, b) -> a + b // 생략 후
// 괄호 생략(1개만) 가능
(x) -> x * 2 // 생략 전
x -> x * 2 // 생략 후
// 중괄호 및 return 생략 가능
(a, b) -> { return a + b; } // 생략 전
(a, b) -> a + b // 생략 후
3. 함수형 인터페이스?
람다를 담는 그릇이 바로 함수형 인터페이스입니다.
추상 메서드가 딱 하나인 인터페이스를 함수형 인터페이스라 부르는데요!
@FunctionalInterface 어노테이션을 붙이면 컴파일러가 규칙을 검증해줍니다.
@FunctionalInterface
public interface MyFunction {
int apply(int a, int b); // 추상 메서드 딱 하나
// default, static 메서드는 여러 개 있어도 OK
default void describe() {
System.out.println("함수형 인터페이스");
}
}
// 람다로 구현
MyFunction add = (a, b) -> a + b;
MyFunction mul = (a, b) -> a * b;
add.apply(3, 4); // 7
mul.apply(3, 4); // 12
4. Java 표준 함수형 인터페이스 4가지
Java가 java.util.function패키지에 미리 만들어 놓은 인터페이스들이에요.
직접 만들 필요 없이 이걸 쓰면 됩니다.

//Function<T, R>
Function<String, Integer> len = s -> s.length();
len.apply("Hello"); // 5
Function<String, String> upper = String::toUpperCase;
upper.apply("hello"); // HELLO;
//Predicate<T>
Predicate<Integer> isEven = n -> n % 2 == 0;
isEven.test(4); // true
//Consumer<T>
Consumer<String> print = s -> System.out.println(s);
print.accept("Hello"); // Hello 출력
//Supplier<T>
Supplier<String> greeting = () -> "Hello";
greeting.get(); // Hello
5. 메서드 참조 (Method Reference)
람다를 더 간결하게 쓰는 방법입니다!
람다가 단순히 기존 메서드를 호출하기만 한다면 : : 문법으로 줄일 수 있습니다.

List<String> names = List.of("Charlie", "Alice", "Bob");
// 정적 메서드 참조
Function<String, Integer> parse = Integer::parseInt;
// 임의 객체 인스턴스 메서드 참조
names.stream()
.map(String::toUpperCase) // s -> s.toUpperCase()
.forEach(System.out::println); // s -> System.out.println(s)
// 특정 인스턴스 메서드 참조
String prefix = "Hello, ";
Function<String, String> greet = prefix::concat;
greet.apply("Alice"); // "Hello, Alice"
// 생성자 참조
Supplier<ArrayList<String>> listFactory = ArrayList::new;
ArrayList<String> newList = listFactory.get();
// 파라미터 있는 생성자 참조
Function<String, StringBuilder> sbFactory = StringBuilder::new;
StringBuilder sb = sbFactory.apply("초기값");
7. 함수 합성 (Function Composition)
함수형 인터페이스에는 함수를 조합하는 메서드가 내장되어 있습니다.
Function<Integer, Integer> times2 = n -> n * 2;
Function<Integer, Integer> plus10 = n -> n + 10;
// andThen — 왼쪽 먼저 실행 후 오른쪽
Function<Integer, Integer> times2ThenPlus10 = times2.andThen(plus10);
times2ThenPlus10.apply(5); // (5 * 2) + 10 = 20
// compose — 오른쪽 먼저 실행 후 왼쪽
Function<Integer, Integer> plus10ThenTimes2 = times2.compose(plus10);
plus10ThenTimes2.apply(5); // (5 + 10) * 2 = 30
// Predicate 조합
Predicate<Integer> isPositive = n -> n > 0;
Predicate<Integer> isEven = n -> n % 2 == 0;
Predicate<Integer> isPositiveEven = isPositive.and(isEven);
isPositiveEven.test(4); // true
isPositiveEven.test(-2); // false
isPositiveEven.test(3); // false
Predicate<Integer> isPositiveOrEven = isPositive.or(isEven);
isPositiveOrEven.test(-2); // true (짝수라서)
Predicate<Integer> isNegative = isPositive.negate();
isNegative.test(-3); // true
8. 예제
람다를 활용한 전략 패턴
람다가 나오기 전에는 전략 패턴을 구현하려면 클래스를 여러 개 만들어야 했죠.
람다로 훨씬 간결하게 구현할 수 있습니다.
할인 전략을 람다로 표현하기
@FunctionalInterface
interface DiscountPolicy {
double apply(double price);
}
public class Order {
private double price;
private DiscountPolicy policy;
public Order(double price, DiscountPolicy policy) {
this.price = price;
this.policy = policy;
}
public double finalPrice() {
return policy.apply(price);
}
}
// 사용 — 클래스 없이 람다로 전략을 바로 주입
DiscountPolicy none = price -> price;
DiscountPolicy ten = price -> price * 0.9; // 10% 할인
DiscountPolicy fixed = price -> price - 1000; // 1000원 할인
DiscountPolicy vip = price -> price * 0.7; // 30% 할인
Order o1 = new Order(10000, ten);
Order o2 = new Order(10000, vip);
System.out.println(o1.finalPrice()); // 9000.0
System.out.println(o2.finalPrice()); // 7000.0
// 동적으로 전략 선택
String grade = "VIP";
DiscountPolicy policy = switch (grade) {
case "VIP" -> price -> price * 0.7;
case "GOLD" -> price -> price * 0.85;
default -> price -> price;
};
핵심 정리
아무래도 람다의 핵심은 코드를 값처럼 전달하는 것인듯 하죠?
함수형 인터페이스가 그릇 역할을 하고, 메서드 참조는 람다를 더 간결하게 줄이는 문법입니다.
람다는 스트림 API의 근간이 되기 때문에 확실하게 이해하고 넘어가야 할 것 같습니다!
'Language > Java' 카테고리의 다른 글
| [Java] Enum 파헤치기 (0) | 2026.04.25 |
|---|---|
| [Java] 스트림 API(Stream API) 완전히 파헤치기 (0) | 2026.04.23 |
| [Java] 제네릭(Generic) 완전히 파헤치기 (0) | 2026.04.23 |
| [Java] 컬렉션 프레임워크 (0) | 2026.04.23 |
| [Java] 예외 처리 (Exception Handling) (0) | 2026.04.23 |