언어, 프레임워크/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());  // 랜덤 값 출력