Language/Java

[Java] 람다식

Hoplin 2022. 3. 16. 00:59
반응형

람다식이란

람다식이란 간단히 말해 메소드를 하나의 '식'(expression) 으로 표현한 것이다.람다식은 함수를 간략하면서도 명확하게 표현할 수 있다는 장점이 있다. 메소드를 람다식으로 표현하면, 메소드 이름, 반환타입이 없어지므로 익명함수(Anonymous Function)이라고도 부른다.예를들어 아래와 같이 사용할 수 있다.

int[] arr = new int[5];
Arrays.setAll(arr,(i) -> (int)(Math.random() * 5) + 1);

람다식 작성하기

람다식의 기본 형태는 아래와 같이 생겼다

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

다만 여기서 반환값이 있는경우에서 두가지 경우로 작성해줄 수 있다.

  1. return문을 포함한 람다식 (statement가 된다. ;를 붙여야 된다. {}를 꼭 써주어야한다.)
(int a, int b) -> {return a > b ? a : b;}

  2. return문을 포함시키지 않은 람다식(expression이 된다. ; 를 붙이지 않아도 됨. {}를 써주면 안된다.)

(int a, int b) -> a > b?a:b

 

statement형태의 람다식에서 {}를 생략할 수 있는 경우는 문장이 하나인 경우, 생략할 수 있다.

@FunctionalInterface
interface MyFunction1{
    void myMethod();
}

MyFunction1 f1 = () -> System.out.println("Hello World"); // ok

 

다만 return문은 하나의 문장이어도 생략해서는 안된다.

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

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

 

함수형 인터페이스

람다식은 그렇다면 어떤 참조타입을 가질까? 람다식 또한 참조형이므로, 클래스, 인터페이스 모두 가능하다. 단, 람다식과 동일한 메소드가 정의되어있어야 하는것이다. 그래야만 참조변수로 익명 객체의 메소드를 호출할 수 있기 때문이다. 예를 들어 아래와같은 인터페이스가 있다고 가정하자

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

이 인터페이스를 구현한 익명 클래스 객체는 다음과 같이 생성할 수 있는것이다.

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

인터페이스에 정의된 람다식 선언부와 max()의 선언부가 일치한다. 이를 람다식으로 아래와 같이 표현할 수도 있다.

class t{
    public static void main(String[] args){
        MyFunction f = (int a, int b) -> {return a>b ? a : b;};
        int c = f.max(10,20);
    }
}

인터페이스를 구현한 익명 객체를 람다식으로 대체가 가능한 이유는 

  • 람다식도 실제로는 익명객체이다
  • 익명객체 메소드와 람다식의 매개변수타입, 개수, 반환값이 모두 일치하다

와 같은 이유로 대체가 가능하다. 이와 같은 이유로 인터페이스를 통해 람다식을 다루기로 결정하였으며, 람다식을 다루기 위한 인터페이스를 '함수형 인터페이스' 라고 부르기로 했다. 함수형 인터페이스 선언은 인터페이스 위에 어노테이션 '@FunctionalInterface'를 붙여주면된다.

@FunctionalInterface
interface MyFunction1{
    void myMethod();
}

단, 이 함수형 인터페이스는 '오직 하나의 추상메소드만 정의되어있어야 한다' 라는 제약이 있다. 이유는, 이래야 람다식, 인터페이스 메소드가 1 : 1 매핑이 되기 때문이다.

 

함수형 인터페이스 타입의 매개변수와 반환타입

아래와 같은 함수형 인터페이스가 정의되어있다고 가정하자

@FunctionalInterface
interface MyFunction1{
    void myMethod();
}

만약 메소드 매개변수가 함수형 인터페이스인 MyFunction1이면 어떻게 해야할까? 이는 이 메소드를 호출할 때 람다식을 참조하는 참조변수를 매개변수로 지정해야한다는 의미이다. 아래와 같이 MyFunction1타입 매개변수를 받는 aMethod라는 메소드가 있다고 가정하자

class lambda_test{
    void aMethod(MyFunction1 f){
        f.myMethod();
    }
}

이 메소드에는 아래와 같이 두가지 방법으로 넘겨줄 수 있다. 하나는 함수형 인터페이스를 구현한 참조변수를 넘겨줄 수 도 있고, 다른 한 방법은 직접 익명함수를 넘겨주는 방법도 있다.

class t{
    public static void main(String[] args){
        lambda_test lt = new lambda_test();

        // 방법 1
        MyFunction1 f = () -> System.out.println("Hello World");
        lt.aMethod(f);

        //방법 2
        lt.aMethod(() -> System.out.println("Hello World"));
    }
}

 

그리고 반대로 반환타입이 함수형 인터페이스인 경우를 살펴보자. 이런 경우에도 위와 동일하게 함수형 인터페이스를 구현한 참조변수를 반환해도 되고 식을 직접 반환해도 된다.

class lambda_test{
    MyFunction1 getMyFunction1_1(){
        MyFunction1 f = () -> System.out.println("Hello Wolrd");
        return f;
    }
    MyFunction1 getMyFunction1_2(){
        return () -> System.out.println("Hello World");
    }
}
반응형