람다 (Lambda)

하마·2025년 3월 4일

Java

목록 보기
6/8

람다 표현식이란?


  • 함수형 프로그래밍을 구성하기 위한 함수식
    - 간단히 말해 자바의 메소드를 간결한 함수식으로 표현한 것
    - 익명 클래스 를 더 간결하게 표현하는 문법
  • 함수형 인터페이스 를 통해서 구현하는 것을 권장
    • 하나의 추상 메소드만 가져야 하기 때문
    • BUT 하나의 추상 메소드를 가진 일반 인터페이스를 통해서도 사용 가능

람다 표현식 작성 방식


  • 컴파일 시점에 컴파일러가 (a, b) -> a + b 표현식을 보고, sum() 메소드를 가진 익명 클래스를 구현함
  • Calculator 인터페이스에 추상 메소드가 하나뿐이기 때문에
    컴파일러는 (a, b) -> a + b 람다 표현식이 sum() 메소드라고 추론

1. 기존 인터페이스 활용

interface IAdd {
    int add(int x, int y);
}

class Add implements IAdd {
    public int add(int x, int y) {
        return x + y;
    }
}
        
public class Main {
    public static void main(String[] args) {
        // 생 클래스로 메소드 사용하기
        Add a = new Add();
        
        int result1 = a.add(1, 2);
        System.out.println(result1);
    }
}
  1. 인터페이스 작성 후
  2. 인터페이스의 추상 메소드 구현
  3. 객체 생성 후 add() 메소드 사용

2. 익명 클래스 사용

interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        // 익명 클래스로 정의해 사용하기 (일회용)
        Iadd a = new IAdd() {
            public int add(int x, int y) {
                return x + y;
            }
        };
        
        int result2 = a.add(1, 2);
        System.out.println(result2);
    }
}

3. 람다 표현식 사용

@FunctionalInterface
interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        // 람다 표현식으로 함축 하기
        IAdd lambda = (x, y) -> {
            return x + y;
        }; // 람다식 끝에 세미콜론을 잊지말자

        int result3 = lambda.add(1, 2);
        System.out.println(result3);
    }
}

아무 클래스나 추상 클래스의 메소드를 람다식으로 줄이는 등의 행위는 못한다.
오로지 인터페이스로 선언한 익명 구현 객체만이 람다식으로 표현할 수 있다.
그리고 람다 표현이 가능한 인터페이스를 가리켜 함수형 인터페이스라 한다.

⚠️ 람다 사용 시 주의사항

함수형 인터페이스 어노테이션을 활용합시다.

  • 함수형 인터페이스 는 단 하나의 추상 메서드만 가지도록 강제하는 어노테이션입니다.
  • 인터페이스에 두 개 이상의 추상 메서드가 존재하면 컴파일러가 어떤 메서드를 구현하는지 모호해지기 때문입니다.
    • 같은 이름의 sum() 메서드를 여러 형태로 정의한다면
      람다 표현식이 어떤 메서드를 구현하는 것인지 명확하지 않아
      모호성이 발생할 수 있습니다.
// 함수형 인터페이스가 될 수 있음
public interface Calculator {
    int sum(int a, int b);
}
// 함수형 인터페이스가 될 수 없음
public interface Calculator {
    int sum(int a, int b);
    int sub(int a, int b);
}
@FunctionalInterface // ✅ 함수형 인터페이스 선언
public interface Calculator {
    int sum(int a, int b); // ✅ 오직 하나의 추상 메서드만 선언해야합니다.
    int sum(int a, int b, int c); // ❌ 선언 불가. 에러발생!
}

람다식을 매개변수로 전달하는 방법


1. 익명 클래스를 변수에 담아 전달

  • 람다식 없이 직접 객체를 생성해서 전달하는 방식
  • 클래스의 익명 객체를 만든 후 매개변수로 전달
public class Main {
    public static int calculate(int a, int b, Calculator calculator) {
        return calculator.sum(a, b);
    }

    public static void main(String[] args) {
        Calculator cal = new Calculator() {
            @Override
            public int sum(int a, int b) {
                return a + b;
            }
        };

        // ✅ 익명 클래스를 변수에 담아 전달
        int ret = calculate(3, 3, cal);
        System.out.println("ret = " + ret); // 출력: ret1 = 6
    }
}

2. 람다식을 변수에 담아 전달

  • 람다식을 변수에 담아 매개변수로 전달하는 방식
  • calculate() 메소드의 매개변수 타입이 Calculator
    • 인터페이스를 구현했는지 추론되기 때문에 람다식 전달 가능
public class Main {
    public static int calculate(int a, int b, Calculator calculator) {
        return calculator.sum(a, b);
    }

    public static void main(String[] args) {
        Calculator cal = (a, b) -> a + b;
        
        // ✅ 람다식을 변수에 담아 전달
        int ret = calculate(4, 4, cal);
        System.out.println("ret = " + ret); // 출력: ret4 = 8
    }
}

3. 람다식을 직접 전달

  • 람다식을 직접 전달
  • 마찬가지로 calculate() 메소드의 매개변수 타입이 Calculator
    • 인터페이스를 구현했는지 추론되기 때문에 람다식 전달 가능
public class Main {

    public static int calculate(int a, int b, Calculator calculator) {
        return calculator.sum(a, b);
    }

    public static void main(String[] args) {
        Calculator cal = (a, b) -> a + b;
        
        // ✅ 람다식을 변수에 담아 전달
        int ret = calculate(4, 4, cal);
        System.out.println("ret = " + ret); // 출력: ret4 = 8
    }
}

람다 표현식 활용하기


다양한 람다식의 할당

람다식의 가장 큰 특징은 변수에 정수를 할당하듯이 함수를 할당할 수 있다는 것이다.

함수도 일반 데이터처럼 메모리 주소가 할당되어 있다.
지금까지 클래스를 통해 메소드를 사용해왔기에 메모리 주소를 변수에 할당하는 일은 없었지만, 람다식을 사용하면 C 같은 함수 스타일의 프로그래밍이 가능해진다.

1. 람다식을 변수에 할당

interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        IAdd lambda = (x, y) -> x + y; // 함수를 변수에 할당
        lambda.add(1, 2); // 함수 사용
    }
}

2. 람다식을 매개변수로 할당

특히 람다식은 메소드의 매개변수에 바로 입력값으로 넣는 방식으로 정말 많이 사용된다.

interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        int n = result((x, y) -> x + y); // 메소드의 매개변수에 람다식을 전달
        System.out.println(n); // 3
    }

    public static int result(IAdd lambda) {
        return lambda.add(1, 2);
    }
}

기존의 메소드같은경우 변수에 할당하거나, 매개변수로 넣거나, 반환값으로 사용하는 행위는 꿈도 못 꿔왔을 것이다.
하지만 람다식이 이런 식으로 응용이 가능한 이유는 람다는 익명 함수이며, 익명 함수는 모두 일급 객체로 취급되기 때문이다.

3. 람다식을 반환값으로 할당

interface IAdd {
    int add(int x, int y);
}

public class Main {
    public static void main(String[] args) {
        IAdd func = makeFunction(); // 메소드의 반환값이 람다 함수
        int result = func.add(1, 2);
        System.out.println(result); // 3
    }

    public static IAdd makeFunction() {
        return (x, y) -> x + y;
    }
}

참고자료


챕터 3-5 : 람다(Lambda)
람다 표현식 완벽 정리

0개의 댓글