본문 바로가기
Programming/JAVA

[Java] 람다식 (Lambda Expression)

by 코딩의성지 2021. 8. 16.

오늘은 람다식에 대해 간단하게 정리해두려고 한다.

람다식은 Java 8 부터 지원하는 객체지향 프로그래밍과는 다른 함수형 프로그래밍 방식이다.

람다식은 익명객체를 더 간단하게 줄여서 사용하는 방식이라고 생각하면 된다.

 

람다식에서는 함수명이나 반환타입을 제거하고

 

(파라미터, ....) -> { 실행문 }

 

이형태로 사용하면 된다.

 

백문이 불여일타 이기 때문에 간단한 예제를 코딩으로 구현해보도록 하겠다.

 

우선 앞서 말한것 처럼 람다식이 익명 객체라고 했었다.

람다식을 사용하기 위해서는 우선 함수형 인터페이스를 만들어줘야한다.

 

함수형 인터페이스 

@FunctionalInterface
interface MyFunction {
    int max(int a, int b);
}

코드를 보시면 @FunctionalInterface 어노테이션을 달아놨는데 안달아도 상관없지만, 없으면 두개 이상의 추상메서드를 선언할 수 있다. 함수형 인터페이스는 단 하나의 추상메서드만이 있어야 함으로 왠만하면 어노테이션을 달아주자. 

 

이렇게 선언한 뒤 기존의 익명 객체는 아래와 같은 방법으로 사용을 할 수 있다.

 

익명 객체

        MyFunction f = new MyFunction() {
            @Override
            public int max(int a, int b) {
                return a > b ? a : b;
            }
        };

        int max = f.max(3,5);

이러한 형태를 람다로간단하게 바꿀수 가 있다. 아래 코드를 보자.

 

람다식

        MyFunction f = (a,b) -> a > b ? a: b;
        
        int max = f.max(3,5);

정말 신기하게도 위처럼 코드를 짜면 실제로 max 메서드 호출시 람다식이 호출되게 된다. 함수형 인터페이스 타입의 참조변수로 람다식을 참조할 수 있기 때문이다. 여기서 조금 주의해야할 점은 함수형 인터페이스의 메서드와 람다식의 매개변수 갯수 및 반환 타입이 일치해야 한다는 것이다.

 

람다식을 이용하면 아래의 코드처럼 코드 라인 자체를 확 줄여버릴 수 있다.

아래의 코드는 String 을 정렬하고자할 때 Compartor 익명 객체를 생성하여 내용을 작성하는 코드이다.

 

람다식 사용 예제

	List<String> names = Arrays.asList("kang", "kim", "park", "choi", "lee");

        Collections.sort(names, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o2.compareTo(o1);
            }
        });

위와 같은 이러한 코드를 람다식으로 대체하면 아래와 같게 변한다.

        List<String> names = Arrays.asList("kang", "kim", "park", "choi", "lee");

        Collections.sort(names, (s1, s2) -> s2.compareTo(s1));

 

 

람다 캡처링(Lambda Capturing)  문제

여기서 한가지만 더 언급하고 가겠다.

아래 작성한 코드를 보자.

 

다음코드를 보면 sum 이라는 변수를 final 변수나 effectvely final 변수로 해야한다는 에러가 떨어지는 것을 볼수 있다. 여기서 이 외부에 선언된 sum이라는 변수를 자유변수라고 하는데, 자유변수느는 람다식 내부에서는 변경이 불가능한 final ( 선언과 동시에 초기화 한후 변경 불가) 이나 effectively final 변수(재할당 불가능 변수) 여야 한다.

 

이렇게 자유변수를 내부에서 참조하는 행위를 람다 캡처링(Lambda Capturing) 혹은 변수 캡처링(Variable Captureing) 이라고 한다. 

 

여기서 자유변수가 final 이나 effectively final 이어야하는 이유가 있다.

전에 내가 JVM 메모리 구조에 대해 포스팅한적이 있는데,

https://devkingdom.tistory.com/226

 

[JAVA] JAVA 메모리 이야기 - Stack 과 Heap

하이.. ! 어느날 회사의 누군가 Java의 메모리가 어떻게 관리되는지에 대해서 물어봤다. 대답이 많이 나오지 않았다... 나름대로 Java를 제일 잘한다고 생각했었고, 자신감도 있던 상태라 충격이 컸

devkingdom.tistory.com

 

여기서 아시는 것처럼 static 변수는 method 영역에 저장이 되고, 객체로 생성되는 것들은heap 영역에 저장이 되고, 지역변수는 stack 영역에 저장된다고 말했었다.

 

그런데 스택영역의 경우 다른영역과 다르게 스레드마다 별도 스택영역이 생성되고 별도로 공유되는 영역이 아니다.

람다식 내부로직은 별도의 스레드에서 실행이 되기 때문에 지역변수를 선언한 스레드에서 해당 변수를 참조하기 위해서 자신의 스택에 해당 데이터를 복사하게 된다.

 

만약에 여러 스레드에서 동일한 람다식을 사용한다면 람다 캡처링은 진행이 되지만, 복사된 지역변수를 변경하기 때문에 데이터 동기화가 안된다.

 

그래서 람다식 내부에서 사용하는 변수는 변하지 않는 변수인 final 변수나 effectvely final 변수여야 한다는 것이다. 그래서 해당변수는 지역변수가 아니라 인스턴스 변수나 스태틱 변수로 사용을 해줘야한다. 

 

오늘은 람다식에 관하여 간단하게 다루어봤는데 도움이 많이 되셨으면 좋겠다.

 

끝 !

반응형

'Programming > JAVA' 카테고리의 다른 글

[Java] 멀티 스레드 동시성 제어  (1) 2021.09.02
[Java] Multi Thread 처리  (1) 2021.08.30
Apache Kafka 정리  (1) 2021.08.19
[Java] JVM GC 기본 동작 방식 이해하기  (0) 2021.08.04
[JAVA] JAVA 메모리 이야기 - Stack 과 Heap  (14) 2021.04.24

댓글