[Java 8] 람다 표현식

홍정완·2022년 5월 29일
0

Java

목록 보기
3/25
post-thumbnail

람다 표현식은 컴파일러의 추론에 의지해서 코드를 단순화하는 것


  • 메서드를 하나의 식으로 표현한 것



람다


  • (인자 리스트) -> {바디}

    • 바디가 한 줄로 표현될 경우 {}와 return 문 생략 가능
    • 하지만 여러 줄이라면 {}와 return 문을 명시적으로 선언



인자 리스트


1. 인자가 없을 경우


Supplier<Integer> getNumber = () -> 20;



2. 인자가 하나인 경우


UnaryOperator<Integer> plus20 = (i) -> i + 20;



3. 인자가 두 개인 경우


BinaryOperator<Integer> getSum = (x, y) -> x + y;



4. 인자 타입


BinaryOperator<Integer> getSum = (Integer x, Integer y) -> x + y;

  • 인자 리스트의 타입을 컴파일러가 추론하기 때문에 생략 가능

    • 하지만 위의 예시처럼 명시할 수도 있다.



변수 캡처 (Variable Capture)


  • 람다 표현식에서 외부 변수를 참조할 때 그 값을 복사해두는 것

public class Dummy {

    public static void main(String[] args) {

        Dummy dummy = new Dummy();
        dummy.run();

    }

    private void run() {

        // 지역 변수
        int baseNumber = 20;

        // 람다 표현식 외부의 값, baseNumber 참조
        IntConsumer printInt = (i) -> System.out.println(i+baseNumber);

        printInt.accept(15);

    }

}

지역변수를 참조하고 있을 때, 변수 캡처가 일어난다.


  • 람다는 별도의 쓰레드에서 실행 가능

  • 지역 변수는 JVM의 메모리 중 Stack 영역에 저장


    • 각 쓰레드는 별도의 Stack을 가지고 있다

    • 서로 다른 쓰레드 간 Stack 영역이 공유되지 않는다



만약, 람다의 쓰레드가 종료되기 전 참조하고 있는 지역변수의 쓰레드가 먼저 종료되어,
지역변수가 Stack에서 사라진다면 이는 오류로 이어질 수 있다

따라서 변수 캡처(Variable capture)를 통해 자신의 스택에 참조하고 있는 변수를 복사해두어 오류를 방지한다



  • 값을 복사해오기 때문에 참조되는 변수는 변경되면 안 된다

    • 참조되는 변수의 값을 변경하려고 하면 concurrency 문제가 발생할 수 있어 컴파일 에러가 발생




effective final


  • Java8 이후로는 참조 변수가 final처럼 동작한다면 생략 가능하도록 변경

    • 이를 effectively final

  • 변수 캡처는 람다 표현식뿐 아니라 익명 클래스나 내부 클래스에서도 사용



// ex)
private void run() {

        int baseNumber = 20;

        // 내부 클래스
        class LocalClass {
            void printBaseNumber() {
                System.out.println(baseNumber);
            }
        }

        // 익명 클래스
        Consumer<Integer> integerConsumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(baseNumber);
            }
        };

        // 람다 표현식
        IntConsumer printInt = (i) -> System.out.println(i + baseNumber);

        printInt.accept(20);
    }



차이점


내부 클래스와 익명 클래스는 '쉐도잉'한다.




쉐도잉


  • 같은 변수명이 존재할 경우 스코프에 의해 외부 참조가 가려지는 것

  • 람다의 스코프는 외부 변수와 같기 때문에 변수를 가질 수 없다.

    • 그렇기 때문에 쉐도잉이 일어나지 않는다
    • 같은 스코프에 같은 이름의 변수를 2개 선언하려는 것과 같기 때문에 애초에 컴파일 오류 발생
profile
습관이 전부다.

0개의 댓글