Haneul's Blog

[Java] 람다식 본문

Java

[Java] 람다식

haneulss 2022. 10. 27. 21:07

목표 [백기선 자바 스터디 15주차]

자바의 람다식에 대해 학습하세요.

학습할 것

  • 람다식 사용법
  • 함수형 인터페이스
  • Variable Capture
  • 메소드, 생성자 레퍼런스

 

람다식 사용법

람다식이란?

람다식은 메서드를 하나의 식으로 표현한 것입니다. 함수를 간략하면서도 명확하게 표현할 수 있게 해주며 메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어져서 람다식을 익명 함수라고도 합니다.

 

자바에서의 모든 메서드는 클래스에 포함되어야 해서 일반적으로는 클래스를 만들고 객체를 생성해야 메서드를 호출할 수 있습니다.
하지만 람다식은 오직 람다식 자체만으로 메서드의 역할을 할 수 있게 해줍니다.

 

람다식 작성법

(a, b) -> {return a + b}; // 일반적인 람다식

(a, b) -> a + b; // 따로 로직이 없고 반환값만 있다면 {} 생략 가능

a -> {return a + 3} // 파라미터가 하나라면 괄호도 생략 가능

람다식에 선언된 매개변수의 타입은 추론이 가능한 경우에 생략할 수 있습니다. 대부분의 경우 생략이 가능하고 람다식에 반환타입이 없는 이유도 항상 추론이 가능하기 때문입니다.
주의해야 할 점은 두 매개변수 중 어느 하나의 타입만 생략하는 것은 허용되지 않습니다.

 

함수형 인터페이스

함수형 인터페이스는 반드시 단 한개의 추상 메서드만을 가져야 하는 인터페이스입니다.

이를 왜 사용하는 것일까요?

사실 람다식은 함수형 인터페이스로만 접근이 가능합니다. 위에서 말했다시피 자바는 클래스를 만들고 그 안에 메서드를 만들어야만 메서드를 사용할 수 있습니다. 그래서 람다식도 따로 별도의 방법을 사용한 것이 아닌 결국엔 인터페이스의 추상 메서드를 통해서 사용할 수 있는 것입니다.
결국 람다식은 함수와 아예 똑같다고 할 순 없고 익명 클래스의 객체와 동등하다고 할 수 있습니다.

 

함수형 인터페이스 정의 하는 법

public interface Adder {
    public int add(int a, int b);
}

Adder adder = (a, b) -> a + b;
System.out.println(adder.add(1, 2)); // 결과 -> 3

 

기본 함수형  인터페이스

출처: https://woodcock.tistory.com/3

 

Variable Capture

람다 캡쳐링

람다의 바디에서는 파라미터가 아닌 바디 외부에 있는 변수를 참조할 수 있습니다. 유사하게 로컬 클래스, 익명 클래스에서도 참조가 가능한데, 람다 시그니처의 파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수를 자유 변수라고 합니다.
그리고 람다 바디에서 자유 변수를 참조하는 것을 람다 캡쳐링(Lambda Capturing)이라고 합니다.

람다 캡쳐링의 제약 조건

지역 변수를 람다 캡쳐링할 때 자유 변수는 두 가지 제약 조건이 있습니다.

  1. 자유 변수는 final로 선언되어야 한다.(java 8이전)
  2. final로 선언되지 않은 자유 변수는 final처럼 동작해야 한다.(effectively final)

그렇다면 위의 제약 조건은 왜 있는 걸까요?
지역 변수는 JVM의 stack 영역에 생성되고, 쓰레드 별로 이 stack 영역이 별도로 생성됩니다. 즉, 지역 변수는 쓰레드끼리 공유가 되지 않지만 인스턴스 변수는 힙 영역에서 생성되기 때문에 쓰레드끼리 공유가 가능합니다.

람다는 별도의 쓰레드에서 실행이 가능하기 때문에 자유 변수가 있는 쓰레드가 동작하고 있지 않다면 람다가 그 자유 변수를 참조할 수 없기 때문에 오류가 날 것입니다. 
하지만 람다에서는 자유 변수 참조가 가능한데 왜 그런 것일까요?

variable capture란?

이는 람다(또는 로컬 클래스, 익명 클래스)가 자유 변수를 참조할 때 직접 그 변수를 참조하는 것이 아니라 자유 변수를 자신의 stack에 복사하여 참조하기 때문에 가능한데, 이를 variable capture라고 합니다.

 

그렇기 때문에 variable capture가 될 자유 변수는 수정이 불가하도록 final이거나 effectively final이어야 합니다.
자바 8 이전에는 이런 이유로 final이 아닌 변수는 익명/로컬 클래스, 암다에서 참조하지 못했는데 자바 8 이후로는 final을 붙이지 않아도 effectively final이기만 해도됩니다.

 

메서드, 생성자 레퍼런스

메서드 레퍼런스(메서드 참조)

메서드를 간결하게 지칭할 수 있는 방법으로 람다가 쓰이는 곳에서 사용 가능하며 일반 함수를 람다 형태로 사용할 수 있도록 해줍니다.

 

interface Adder {
    int add(int a, int b);
}

public class Test {
    public static void main(String[] args) {
        Adder adder = Test::addPrint;
        System.out.println("adder.add(2, 3) = " + adder.add(2, 3));
    }

    private static int addPrint(int a, int b) {
        return a + b;
    }

}

 

static 메서드 참조

클래스이름::메서드이름
(매개변수) -> Class.staticMethod(매개변수)

String::valueOf
str -> String.valueOf(str)

인스턴스 메서드 참조

클래스이름::메서드이름
(obj, 매개변수) -> obj.instanceMethod(매개변수)

String::length
(value) -> value.length();

특정 객체 인스턴스 메서드 참조

obj::instanceMethod
(매개변수) -> obj.instanceMethod(매개변수)

object::toString
() -> object.toString()

'Java' 카테고리의 다른 글

[Java] TDD란?  (0) 2022.11.03
[Java] 제네릭  (0) 2022.10.26
[Java] I/O  (0) 2022.10.25
[Java] 애노테이션  (0) 2022.10.24
[Java] Enum  (0) 2022.10.23