언어, 프레임워크/Java
익명 클래스 / 람다 (Lambda) / 함수형 인터페이스
go_getter
2025. 2. 26. 22:58
익명 클래스
- 이름이 없는 클래스
- 별도의 클래스 파일을 만들지 않고, 코드 내에서 일회성으로 정의해서 사용
- 인터페이스 / 일반 클래스 / 추상 클래스의 구현과 상속을 위해 익명 클래스 활용 가능
- 람다는 인터페이스 구현을 위해 익명 클래스 활용
- 아래 코드는 인터페이스 객체를 생성하는 것이 아니라, 인터페이스를 구현한 익명 클래스의 객체를 생성
public interface Calculator {
int sum(int a, int b);
}
public class Main {
public static void main(String[] args) {
// ✅ 익명 클래스 활용
Calculator calculator1 = new Calculator() {
@Override
public int sum(int a, int b) {
return a + b;
}
};
int ret1 = calculator1.sum(1, 1);
System.out.println("ret1 = " + ret1);
}
}
람다 (Lambda)
- 자바 8에서 도입
- 익명 클래스를 더 간결하게 표현하는 문법
- 함수형 인터페이스(=하나의 추상 메서드만 가지는 인터페이스)로 구현
- 컴파일러가 내부적으로 람다식을 익명 클래스로 변환
기본 구조
(매개변수들) -> {실행할 코드}
- 매개변수가 하나인 경우는 괄호 생략 가능
- 단일 표현식이라면 중괄호 생략 가능
예시
(int a, int b) -> { a + b; }
int a -> a + 1;
메서드 참조 형식으로 람다 표현식 변환
`ClassName::methodName`
// 람다 표현식
List<String> list = Arrays.asList("a", "b", "c");
list.forEach(s -> System.out.println(s));
// 메소드 참조
list.forEach(System.out::println);
주의 사항
1) 람다식 내의 this는 람다를 정의한 객체가 아니라, 람다의 외부 클래스의 인스턴스
public class LambdaTest {
public void test() {
Runnable r = () -> System.out.println(this); //this는 LamdaTest의 인스턴스
r.run();
}
}
2) 값의 변경이 없는 외부 변수만 람다식에서 사용 가능 (외부 변수 캡처)
int a = 10;
Runnable r = () -> System.out.println(a); // a는 final이어야만 사용 가능
r.run();
함수형 인터페이스
- annotation `@FunctionalInterface` 사용해서 함수형 인터페이스로 구현하는 것 권장
- annotation으로 구현하면, 컴파일러가 하나의 추상 메서드만 가지도록 강제함
- 람다 표현식을 쓸 때, 추상 메서드가 2개 이상이면 람다가 어떤 메서드를 구현하는지 모호해지기 때문
- 디폴트 메서드, static 메서드는 여러 개 들어가도 상관 없음
@FunctionalInterface // ✅ 함수형 인터페이스 선언
public interface Calculator {
int sum(int a, int b); // ✅ 오직 하나의 추상 메서드만 선언해야합니다.
}
public class Main {
public static void main(String[] args) {
...
// ✅ 람다식 활용
Calculator calculator2 = (a, b) -> a + b;
int ret2 = calculator2.sum(2, 2);
System.out.println("ret2 = " + ret2);
}
}
함수형 인터페이스별 람다식 활용 예시
Predicate 인터페이스
- 입력값을 받아서 boolean값을 반환하는 함수형 인터페이스
- 필터링 작업에서 유용
List<String> list = Arrays.asList("apple", "banana", "cherry");
Predicate<String> startsWithB = s -> s.startsWith("b"); // startsWith는 String의 메서드
list.stream()
.filter(startsWithB)
.forEach(System.out::println); // 출력: banana
Consumer 인터페이스
- 입력값을 받아서 처리 후 결과는 반환하지 않는 함수형 인터페이스
List<String> list = Arrays.asList("apple", "banana", "cherry");
Consumer<String> print = s -> System.out.println(s);
list.forEach(print); // 출력: apple, banana, cherry
Function 인터페이스
- 입력값을 받아서 변환된 값을 반환하는 함수형 인터페이스
Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 출력: 25
Supplier 인터페이스
- 입력값이 없고, 결과값만 제공 (랜덤값 생성, 객체 생성)
// 값 반환
Supplier<String> supplier = () -> "Hello, Supplier!";
System.out.println(supplier.get()); // 출력: Hello, Supplier!
// 객체 생성
Supplier<Integer> randomNumberSupplier = () -> (int) (Math.random() * 100); // 0~99 사이의 랜덤 숫자 생성
System.out.println(randomNumberSupplier.get()); // 랜덤 값 출력