Java 람다 표현식 정리

Bruce Han·2022년 11월 11일
0

Java8-정리

목록 보기
3/20
post-thumbnail

이 포스팅의 코드 및 정보들은 강의를 들으며 정리한 내용을 토대로 작성한 것입니다.

람다 표현식

람다

lambda1

위 사진처럼 multiply15라는 추상메서드에 (i) -> i * 15 를 대입하고 있다.
이때 (i)에 들어있는 i인자 리스트이며, i * 15인 부분을 바디라고 한다.

바디가 한 줄이면 중괄호를 없앨 수 있다. 2줄 넘어가면 중괄호로 감싸야 한다.

위의 i * 15를 중괄호로 감싸면

UnaryOperator<Integer> multiply15 = (i) -> {
	return i * 15;
};

이렇게 중괄호 안에 i * 15 값을 return을 한다.

인자 리스트

인자 리스트는 인자가 없을 때, 인자가 한 개일 때, 인자가 여러 개일 때로 나뉜다.

인자가 없을 때

Supplier<Integer> get20 = () -> 10;

괄호 ()안에 아무 인자도 넣지 않고 오로지 반환만 한다.
Supplier 인터페이스가 대표적이다.

인자가 한 개일 때

lambda2

타입은 하나인데 구현에 쓰이는 타입은 세 개이다.
이는 하나의 타입으로 세 개 모두 타입을 동일하게 쓴다는 의미이다.

저렇게 선언한 제네릭 타입을 가지고 컴파일러가 추론(infer)할 수 있지만
lambda3

(Integer i, Integer j)이런 식으로 각 인자의 타입을 지정할 수 있다.
하지만 컴파일러가 추론할 수 있도록 생략하면 보기에도 문제 없기에, 굳이 인자에까지 타입을 명시할 필요는 없다.
타입은 이미 변수 선언부에 정의 되어있기 때문이다.

인자가 여러 개일 때

lambda4

3개의 타입이 있는 인터페이스가 있다. 이 3개가 모두 인자는 아니고, 앞의 두 개는 인자, 남은 한 개는 반환 타입이다.

이는 인자의 개수를 하나로 줄여 BinaryOperator<T>로 활용할 수 있다.

변수 캡처

lambda5

람다를 감싸고 있는 영역에 있는 local variable이 있다고 가정해보자.

local variable인 baseNumber캡처가 된다.

변수 캡처는?

익명/내부 클래스에서 쓰이던 기능이며, Java 8 이전에는 위와 같은 상황에서 항상 local variable은 앞에 final을 붙여야 했다.

// After Java 8
int afterEight = 10;

// Before Java 8
final int beforeEight = 10;

내부 클래스 간단하게 알아보기

메서드 내부에 정의한 local class

private void foo() {
	class LocalClass { // 내부 클래스
    	void bar() {
           ...
        }
    }
}

lambda6

익명 클래스 간단하게 알아보기

Comsumer<Integer> integerConsumer = new Consumer<Integer>() {
	@Override
    public void accept(Integer integer) {
    		...;
    }
};

lambda7

익명 클래스에서 local variable(baseNumber)을 참고하는 것이다.


다시 돌아가서

private void printIntValue() {
	final 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);
}

로컬 클래스, 익명 클래스, 람다 이 셋의 공통점은 final인 baseNumber를 참조할 수 있다.
Java 8부터는 (예를 들어)baseNumber라는 변수가 사실상 final인 경우, final이라는 키워드를 생략할 수 있는 경우가 있다.
이는 final이라는 키워드는 없지만, 이 변수를 더이상 어디서도 변경하지 않는 경우이다.

자바 document에서는 effective final이라고도 하는데, 이는 방금 설명한 것과 같이 final 없이도 참조가 가능하다.

로컬/익명 클래스가 람다와 다른 점

로컬 클래스와 익명 클래스는 shadowing이 가능하지만, 람다는 shadowing이 되지 않는다.

로컬 클래스나 익명 클래스에 선언한 변수는 그 변수하고 같은 이름의 변수가 printIntValue() 안에 있거나 Main 클래스 안에 있으면 로컬/익명 클래스에 있던 변수가 가린다.
이 두 종류의 클래스는 별도의 scope이 있기 때문이다.

하지만, 람다는 람다를 감싸고 있는 메서드랑만 같게 된다. 즉, shadowing이 일어나지 않는다.

shadowing1

로컬 클래스에 있던 baseNumber가 printIntValue() 안에 있던 baseNumber를 가린 것이다.

shadowing2

익명 클래스에서는 accept()의 파라미터 이름을 baseNumber로 바꾸면 위 사진처럼 printIntValue() 안에 있던 baseNumber가 아닌 파라미터로 받는 baseNumber를 참조하게 된다.

shadowing3

오로지 익명 클래스는 자기 scope안에 있는 파라미터 이름, 즉 printInt.accept(20)에서 넘어 전달되는 그 파라미터를 그대로 쓰게 된다.

하지만 람다는 다르다.

shadowing4

람다 안에 있던 baseNumber는 printIntValue() 안에 미리 선언되어 있던 baseNumber랑 같은 scope이다. 즉, 같은 scope 내에 같은 이름의 변수가 두 개 이상 올 수 없으며, shadowing 현상이 발생되지 않는다는 것이다.

shadowing5

람다식에 있는 인자는 그 람다를 감싸고 있던 printIntValue()에 있던 baseNumber와 같은 범위에 있다는 것이다.

shadowing6

baseNumber의 값을 변경하려고 시도하면, final처럼 쓰고 있었는데 effective final이 아니게 되기 때문에 더이상 참조할 수 없게된다.

정리

람다

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

인자 리스트

  • 인자가 없을 때 : ()
  • 인자가 한 개일 때 : (one) 또는 one
  • 인자가 여러 개일 때 : (one, two)
  • 인자의 타입은 생략 가능하며, 이를 컴파일러가 추론(infer)한다. 다만, 명시할 수도 있다.(Double one, Double two)

바디

  • 화살표 오른쪽에 함수 본문을 정의한다.
  • 여러 줄인 경우에 {}(중괄호)를 사용해서 묶는다.
    - 한 줄인 경우에 {}를 생략할 수 있으며, return도 생략할 수 있다.

변수 캡처 (Variable Capture)

  • 로컬 변수 캡처

    • final이거나 effective final인 경우에만 참조할 수 있다.
    • 그렇지 않을 경우 concurrency 문제가 생길 수 있어서 컴파일러가 방지한다.
  • effective final

    • 이것도 역시 Java 8부터 지원하는 기능으로 "사실상" final인 변수
    • final 키워드를 사용하지 않는 변수를 익명 클래스 구현체나 lambda에서 참조할 수 있다.
  • 익명 클래스 구현체와 달리 'shadowing'하지 않는다.

    • 익명 클래스는 새로 scope을 만들지만, lambda는 lambda를 감싸고 있는 scope과 같다.

Reference

profile
만 가지 발차기를 한 번씩 연습하는 사람은 두렵지 않다. 내가 두려워 하는 사람은 한 가지 발차기를 만 번씩 연습하는 사람이다. - Bruce Lee

0개의 댓글