상세 컨텐츠

본문 제목

람다식 사용법

기록 - 프로그래밍/Java

by wjjun 2024. 2. 2. 01:05

본문

 

익명함수를 생성하기 위한 식으로 함수지향 언어에 가깝습니다. 람다식 형태는 매개 변수를 가진 코드 블록이지만 런타임 시에는 익명 구현 객체를 생성합니다.

 

람다식 -> 매개 변수를 가진 코드 블록 -> 익명 구현 객체

 

예를들어 Runnable 인터페이스가 람다식을 이용하여 익명 구현 개체를 사용한 예시 입니다.

Runnable runnable = new Runnalbe() { // 익명 구현 객체
    public void run() { ... }
}

Runnalbe runnable = () -> { ... }; // 람다식

람다식은 (매개변수) -> {실행코드} 형태로 작성됩니다. 함수 정의 형태를 띠고 있지만 런타임 시에 인터페이스 익명 구현 객체로 생성됩니다. 어떤 인터페이스를 구현할 것인지 대입되는 인터페이스가 무엇이냐에 따라 다릅니다.

 

람다식 기본문법

(타입, 매개변수, ...) -> { 실행문; ... }

(int a) -> { sout(a); }
(a) -> { sout(a); } // 매개변수 타입은 런타임 시 대입 값에 따라 자동 인식
a -> { sout(a); } // 하나의 매개변수만 있다면 괄호 생략이 가능합니다.
() -> { 실행문; ...}
(x, y) -> { return x + y; }
(x, y) -> x + y;

 

함수적 인터페이스

자바는 메서드를 단독으로 선언할 수 없고 항상 클래스 구성 멤버로 선언하기 때문에 람다식은 단순히 메서드를 선언하는 것이 아닌 메서드를 갖고 있는 객체를 생성합니다.

인터페이스 변수 = 람다식;

 

람다식은 인터페이스의 익명 구현 객체를 생성하는 것을 의미합니다. 람다식은 대입될 인터페이스 종류에 따라 작성 방법이 달라지므로 람다식이 대입될 인터페이스를 람다식의 타켓 타입(target type)이라고 합니다.

 

함수적 인터페이스 (@FunctionalInterface)

람다식이 하나의 메서드를 정의하기 때문에 두 개 이상의 추상 메서드가 선언된 인터페이스는 람다식을 이용해서 구현 객체를 생성할 수 없습니다. 하나의 추상 메서드가 선언된 인터페이스만 람다식 타켓 타입이 될 수 있습니다. 이것을 함수적 인터페이스(functional interface)라고 합니다.

 

@FunctionalInterface
public interface MyFunctionalInterface {
    public void method();
    public void otherMethod(); // 컴파일 오류
}

이 어노테이션이 없더라도 하나의 추상 메서드만 있다면 모두 함수적 인터페이스 입니다.

 

매개변수와 리턴값이 없는 람다식

인터페이스를 타겟 타입으로 갖는 람다식은 다음과 같은형태로 작성해야 합니다. 람다식에서 매개변수가 없는 이유는 method()가 매개 변수를 갖지 않기 때문입니다.

MyFunctionalInterface fi = () -> { ... }
fi.method();

public class static void main(String[] args) {
    MyFunctionalInterface fi;
    
    fi = () -> {
        String str = "method call";
        sout(str);
    };
    fi.method();
    
    fi = () -> { sout("method call2"); };
    fi.method();
    
    fi = () -> { sout("method call3"); };
    fi.method();
}

람다식이 대입된 인터페이스의 참조 변수는 다음과 같이 method()를 호출할 수 있습니다. method() 호출은 람다식의 중괄호 {}를 실행시킵니다.

 

매개 변수가 있는 람다식

@FunctionalInterface
public interface MyFunctionalInterface {
    public void method(int x);
}

MyFunctionalInterface fi = (x) -> {...} 또는 x -> {...}
fi.method(5);


public static void main(String[] args) { 
    MyFunctionalInterface fi;
    
    fi = (x) -> {
        int result = x * 5;
        sout(result);
    };
    
    fi.method(2);
    
    fi = (x) -> { sout(x*5); };
    fi.method(2);
    
    fi = x -> { sout(x*5); };
    fi.method(2);
}

 

 

리턴 값이 없는 람다식

//람다식이 매개변수가 두 개인 이유는 method()가 매개 변수를 두 개 갖기 때문입니다. 그리고 method()가 리턴 타입이 있기 때문에 중괄호 { } 에는 return 문이 있어야 합니다.
@FunctionalInterface
public interface MyFunctionalInterface {
    public int method(int x, int y);
}

// 중괄호 {}에 return 문만 있고, return 문 뒤에 연산식이나 메서드 호출이 오는 경우
MyFunctionalInterface fi = (x, y) -> { ... return 값; }

//1-1, 1-2 같다
//2-1, 2-2 같다
// 매개값으로 2와 5를 주면 람다식 x변수에 2, y변수에 5가 대입되고 x와 y는 중괄호 {}에서 사용된다.

// (1-1)
MyFunctionalInterface fi = (x , y) -> {
    return x + y;
}

// (1-2)
MyFunctionalInterface fi = (x , y) -> x + y;



// (2-1)
MyFunctionalInterface fi = (x , y) -> {
    return sum(x ,y);
}

// (2-2)
MyFunctionalInterface fi = (x , y) -> sum(x, y);

 

 

 

클래스 멤버와 로컬 변수 사용

람다식 실행 블록에는 클래스의 멤버(필드와 메서드) 및 로컬 변수를 사용할 수 있습니다. 클래스의 멤버는 제약 사항 없이 사용 가능하지만 로컬 변수는 제약 사항이 따릅니다.

 

클래스의 멤버 사용

람다식 실행 블록에는 클래스의 멤버인 필드와 메서드를 제약 없이 사용할 수 있지만 this 키워드 사용시에는 주의가 필요합니다. 일반적으로 익명 객체 내부에서 this는 익명 객체의 참조이지만, 람다식에서 this는 내부적으로 생성되는 익명 객체의 참조가 아닌 람다식을 실행한 객체의 참조입니다. 다음 예제는 람다식에서 바깥 객체와 중첩 객체의 참조를 얻어 필드값을 출력하는 방법을 나타냅니다.

중첩 객체 inner 에서 람다식을 실행했기 때문에 람다식 내부에서의 this는 중첩 객체 inner 입니다.

public interface MyFunctionalInterface {
    public void method();
}

public class UsingThis {
    public int outterField = 10;
    
    class Inner {
        int innerField = 20;
        
        void method() {
            // 람다식
            MyFunctionalInterface fi = () -> {
                sout(outterField);
                sout(this.outterField); // 바깥 객체의 참조를 얻기 위해서는 클래스명.this 사용
                
                sout(this.outterField); // 람다식 내부에서 this는 Inner 객체를 참조
            };
        }
    }
}

public class UsingThisExample {
    public static void main(String[] args) {
        UsingThis usingThis = new UsingThis();
        UsingThis.Inner inner = usingThis.new Inner();
        inner.method();
    }
}

 

 

로컬 변수사용

람다식에서 바깥 클래스의 필드나 메서드는 제한 없이 사용할 수 있으나 메서드의 매개 변수 또는 로컬 변수를 사용하면 이 두 변수를 final 특성을 가져야 합니다.

public interface MyFunctionalInterface {
    public void method();
}

public class UsingLocalVar {
    void method(int arg) {
        int localVar = 40;
        
        // arg = 31; // fianl 특성때문에 수정 불가
        // localVar = 41 // final 특성때문에 수정 불가
        
        MyFunctionalInterface fi = () -> {
        
            // 로컬변수 읽기 
            sout(arg);
            sout(localVar);
        };
        fi.method();
    }
}

public class UsingLocalVarExam {
    UsingLocalVar ulv = new UsingLocalVar();
    ulv.method(20);
}

 

관련글 더보기

댓글 영역