더 자바, Java8 - 람다 표현식

람다의 구조

pubic class Foo {
    
    // main
    public static void main(String[] args) {
    
        // () 부분 : 인자를 선언
        // -> 이후 부분 : 함수의 바디
        // 바디가 한 줄이면 중괄호를 생략 가능
        UnaryOperator<Integer> plus10 = (i) -> i + 10;
        UnaryOperator<Integer> plus20 = (i) -> {
            i = i + 10;
            i = i + 10;
            return i;
        };
        // 11 출력
        System.out.println(plus10.apply( 1 ));
        // 21 출력
        System.out.println(plus20.apply( 1 ));
        
        // Supplier의 경우, 인자를 받지 않기 때문에 ()부분이 비어 있음
        // 마찬가지로 한줄일 경우 생략 가능
        Supplier<Integer> get10 = () -> 10;
        // 10 출력
        System.out.println( get10.get() );
        
        // BiFuction의 경우, 인자 두개를 받아서 하나의 결과 값을 리턴하는 함수형 인터페이스
        BiFunction<Integer, Integer, Integer> sum1 = (a, b) -> a + b;
        // 인자와 리턴 값이 변수형이 같다면 BinaryOperator<T>로 줄일 수 있다.
        BinaryOperator<Integer> sum2 = (a, b) -> a + b;
        // 20 출력
        System.out.println( sum1.apply(10, 10) );
        // 20 출력
        System.out.println( sum2.apply(10, 10) );
    }
}

람다의 범위

로컬 클래스나 익명 클래스의 경우, 함수 바깥에 있는 변수를 참조할 수 있다.
이 때, 참조하는 baseNumber 변수가 사실상 변경되지 않는 경우 final 키워드를 생략할 수 있다.

public class Foo {
    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.run();
    }
    
    priavte void run() {
        // effective final
        // final 키워드를 생략할 수 있는 경우 : 이 변수가 사실상 final인 경우 (변경되지 않음)
        final int baseNumber = 10;
        
        // 로컬 클래스
        class localClass {
            void printNumber(){
                System.out.println(baseNumber);
            }
        }
        
        // 익명 클래스
        Consumer<Integer> integerConsumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                System.out.println(baseNumber);
            }
        };
    }
}

또한 로컬 클래스나 익명 클래스 내부에 baseNumber 변수를 선언해도 함수의 범위가 다르기 때문에
오류가 발생하지 않으며, 내부의 baseNumber 변수가 우선 시 된다.
이를 쉐도잉 (로컬 클래스 or 익명 클래스 함수 안의 변수가 외부의 변수보다 우선시 됨)이라고 한다.

public class Foo {

    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.run();
    }
    
    private void run(){
        // effective final
        // final 키워드를 생략할 수 있는 경우 : 이 변수가 사실상 final인 경우 (변경되지 않음)
        final int baseNumber = 10;
        
        // 로컬 클래스
        class localClass {
            void printNumber(){
                // 함수의 범위가 다르기 때문에 내부의 baseNumber가 출력되며
                // 오류가 발생하지 않는다.
                int baseNumber = 12;
                // 12 출력
                System.out.println(baseNumber);
            }
        }

        // 익명 클래스
        Consumer<Integer> integerConsumer = new Consumer<Integer>() {
            @Override
            public void accept(Integer integer) {
                // 함수의 범위가 다르기 때문에 내부의 baseNumber가 출력되며
                // 오류가 발생하지 않는다.
                int baseNumber = 13;
                // 13 출력
                System.out.println(baseNumber);
            }
        };
    }
}

하지만 람다의 함수 범위는 상위 run 함수와 같기 때문에 (쉐도잉이 되지 않는다)
baseNumber 변수를 선언할 경우 컴파일 오류가 발생한다.

public class Foo {
    
    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.run();
    }
    
    private void run() {
        
        final int baseNumber = 10;
        .
        .
        .
        // 람다
        IntConsumer printInt = (i) -> {
            // ERROR : Variable baseNumber is already defined in the scope.
//            int baseNumber = 11;
            System.out.println(i + baseNumber);
        };
    }
}

또한 람다 밑에서 baseNumber의 값을 변경하려고 할 경우,
이미 baseNumber는 effective final이기 때문에 컴파일 오류가 발생한다.

public class Foo {
    
    public static void main(String[] args) {
        Foo foo = new Foo();
        foo.run();
    }
    
    private void run() {
        
        final int baseNumber = 10;
        .
        .
        .
        // 람다
        IntConsumer printInt = (i) -> {
            // ERROR
            // Variable used in lambda expression should be final or effectively final
            System.out.println(i + baseNumber);
        };
        
        baseNumber++;
    }
}

출처 :
인프런 강의 - 더 자바, Java 8 (백기선)
https://www.inflearn.com/course/the-java-java8