[자바의 정석] 람다식

YJS·2024년 1월 8일
0

1. 람다식 사용법


람다식이란?

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

→ 메서드의 이름과 반환값이 없어짐 = 익명함수

→ 클래스나 객체 생성 없이 메서드의 역할 수행 가능.

→ 메서드의 매개변수 및 결과로 반환 가능 = 메서드를 변수처럼 다루는 것

💡 람다식 작성 방법

<기존 함수 선언 방법>

반환타입 메서드명 (변수 선언) {
문장들 }

<람다식>

(매개변수 선언) → {
문장들 }

→ 매개변수의 반환 타입이 추론 가능한 경우 생략

→ 선언된 매개변수가 한개인 경우 괄호 생략

(단, 매개변수 타입이 있으면 괄호 생략 불가)

→ {중괄호} 안의 문장이 하나인 경우 생략 가능. (이때 문장의 끝에 ‘;’ 붙이지 않음)

(단, {괄호}안의 문장이 return 문일 경우 생략 불가)

int max(int a, int b){
return a>b ? a:b;}

(int a, int b) -> {return a>b ? a:b;}
(int a, int b) -> a>b? a:b
(a,b) -> a>b? a:b

2. 함수형 인터페이스


람다식을 다루기 위한 인터페이스를 함수형 인터페이스라고 함.

@FunctionalInterface
interface Myfunction{
	public abstract int max(int a, int b);
}

→ 오직 하나의 추상 메서드만 정의되어야 함 (그래야 람다식과 인터페이스가의 메서드가 1:1로 연결)

→ 반면, static과 default 메서드의 개수에는 제약이 없음

 @FunctionalInterface
interface MyFunction {
	void run();  // public abstract void run();
}

class LambdaEx1 {
	static void execute(MyFunction f) { // 매개변수의 타입이 MyFunction인 메서드
		f.run();
	}

	static MyFunction getMyFunction() { // 반환 타입이 MyFunction인 메서드 
		MyFunction f = () -> System.out.println("f3.run()");
		return f;
	}

	public static void main(String[] args) {
		// 람다식으로 MyFunction의 run()을 구현
		MyFunction f1 = ()-> System.out.println("f1.run()");

		MyFunction f2 = new MyFunction() {  // 익명클래스로 run()을 구현
			public void run() {   // public을 반드시 붙여야 함
				System.out.println("f2.run()");
			}
		};

		MyFunction f3 = getMyFunction();

		f1.run();
		f2.run();
		f3.run();

		execute(f1);
		execute( ()-> System.out.println("run()") );
	}
}

-함수형 인터페이스로 람다식을 참조하는 것이지 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 아님. (람다식은 익명 객체이고 익명 객체는 컴파일러가 임의로 이름을 정하기 때문에 타입을 알 수 없음.)

→ 대입 연산자의 양변의 타입을 일치시키기 위해 형변환이 필요

→ 람다식은 Object 타입으로 형변환 불가. 오직 함수형 인터페이스로만 형변환 가능.

java.util.function패키지의 주요 함수형 인터페이스

함수형 인터페이스메서드설명
Runnablevoid run()매개변수도 없고, 반환값도 없음
SupplierT get()매개변수는 없고, 반환값만 있음
Consumervoid accept(T t)Supplier와 반대로 매개변수만 있고, 반환값이 없음
Function<T,R>R apply(T t)일반적인 함수, 하나의 매개변수를 받아서 결과를 반환
Predicateboolean test(T t)조건식을 표현하는데 사용됨. 매개변수는 하나, 반환타입은 boolean

3. Variable Capture


람다식의 실행 블록 내에서 람다식을 감싸고 있는 클래스의 인스턴스 변수, 스태틱 변수, 지역 변수에 접근하는 것이 가능

→ 그러나 지역 변수에 접근할 때는 Variable Capture라는 특별한 작업이 수행되기 때문에 한 가지 제약이 생김

= Variable Capture란 객체 외부에서 선언된 변수를 객체 내부로 복사하는 행위. ****지역 변수 뿐만 아니라 파라미터로 전달된 변수 또한 외부에서 선언된 변수이므로 같은 규칙이 적용

public class CaseOne {

    static int staticVariable = 10;

    int instanceVariable;

    @FunctionalInterface
    interface MyFunction {

        void myMethod();
    }

    public static void main(String[] args) {

        CaseOne caseOne = new CaseOne();
        caseOne.instanceVariable = 20;

        int localVariable = 30;

        MyFunction myFunction = () -> {
            System.out.println("static variable = " + CaseOne.staticVariable);
            System.out.println("instance variable = " + caseOne.instanceVariable);
            System.out.println("local variable = " + localVariable);
        };

        myFunction.myMethod();
    }

}

실행결과
static variable = 10
instance variable = 20
local variable = 30

스태틱 변수나 인스턴스 변수를 변경하는 것은 문제가 없으나, 지역변수를 변경하는 순간 오류가 발생

⇒ 오류가 발생하는 이유는 클래스 내부에 선언된 로컬 클래스(여기선 람다식)가 지역 변수를 참조할 때는 그 값을 복사해서 사용하기 때문(=Variable Capture)

public class CaseOne {

    static int staticVariable = 10;

    int instanceVariable;

    @FunctionalInterface
    interface MyFunction {

        void myMethod();
    }

    public static void main(String[] args) {

        CaseOne caseOne = new CaseOne();
        caseOne.instanceVariable = 20;

        int localVariable = 30;

        // 값 변경
        CaseOne.staticVariable += 5;
        caseOne.instanceVariable += 5;
        localVariable += 5;

        MyFunction myFunction = () -> {
            System.out.println("static variable = " + CaseOne.staticVariable);
            System.out.println("instance variable = " + caseOne.instanceVariable);
            System.out.println("local variable = " + localVariable);    // 컴파일 오류 발생!
        };

        myFunction.myMethod();
    }
}

4. 메소드, 생성자 레퍼런스


1) 메서드 참조

람다식이 하나의 메서드만 호출하는 경우에는 메서드 참조를 통해 간략히 할 수 있음

Function<String , Integer> f = (String s) -> Integer.parseInt(s);

Function<String , Integer> f = Integer::parseInt;

“클래스이름::메서드이름” 혹은 ”참조변수:메서드이름”으로 바꿀 수 있음

2)생성자의 메서드 참조

profile
우당탕탕 개발 일기

0개의 댓글