람다식 (JAVA)

권 해·2023년 1월 27일
0

Java

목록 보기
2/5

지금까지 깃이나 구글에서 다른 사람이 쓴 코드를 참고할 때, 람다식 문법을 사용한 것을 적지 않게 볼 수 있었다. 생각해보면, 나는 람다식을 다른 사람의 코드를 참고하는 것이 아닌 직접 코드를 쓸 때 사용한 적이 한 번도 없었다. 아마 람다식이 왜 사용되는지, 어떤 점이 유용한지 제대로 모르고 있기 때문일 것이다. 그래서 이번에는 람다식에 대해 제대로 알아보려고 한다.

람다식

람다식이란 함수를 하나의 식으로 표현한 것이다. 함수를 람다식으로 표현하면 메소드의 이름이 필요 없기 때문에, 람다식은 익명 함수의 한 종류라고 볼 수 있다.
익명함수(Anonymous Function)란 함수의 이름이 없는 함수로, 익명함수들은 모두 일급 객체이다.
일급 객체란 일반적으로 다를 객체들에 적용 가능한 연산을 모두 지원하는 개체를 가르킨다. 함수를 값으로 사용 할 수도 있으며 파라메터로 전달 및 변수에 대입 하기와 같은 연산들이 가능하다.

람다식을 사용한 간단한 예를 들겠다.

public String hello() {
    return "Hello World!";
}

위 함수를 람다식으로 표현하면,

() -> {return "Hello World!";}

과 같이 간단한 식으로 표현할 수 있다.
() 안에는 함수에 전달할 매개 변수가 들어간다.

람다식의 특징

  • 람다식 내에서 사용되는 지역변수는 final이 붙지 않아도 상수로 간주된다.
  • 람다식으로 선언된 변수명은 다른 변수명과 중복될 수 없다.

장점

  • 코드를 간결하게 만들 수 있다.
  • 식에 개발자의 의도가 명확히 드러나 가독성이 높아진다.
  • 함수를 만드는 과정없이 한번에 처리할 수 있어 생산성이 높아진다.
  • 병렬 프로그래밍이 용이하다.

단점

  • 람다를 사용하면서 만든 무명함수는 재사용이 불가능하다.
  • 디버깅이 어렵다.
  • 람다를 남발하면 비슷한 함수가 중복 생성되어 코드가 지저분해질 수 있다.
  • 재귀로 만들경우에 부적합하다.

여기까지가 람다식에 대한 개념적인 부분이다. 이제 람다식을 사용하기 위해서는 함수형 인터페이스가 무엇인지 알아야 한다.

함수형 인터페이스
함수형 인터페이스란 함수를 1급 객체처럼 다룰 수 있게 해주는 어노테이션으로, 인터페이스에 선언하여 단 하나의 추상 메소드만을 갖도록 제한하는 역할을 한다. 함수형 인터페이스를 사용하는 이유는 Java의 람다식이 함수형 인터페이스를 반환하기 때문이다.
함수형 인터페이스를 구현하기 위해서는 인터페이스를 개발하여 그 내부에는 1개 뿐인 abstract 함수를 선언하고, 위에는 @FunctionalInterface 어노테이션을 붙여주면 된다.

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

public class Lambda {

    public static void main(String[] args) {

        LambdaFunc lambdaFunc = (int a, int b) -> a > b ? a : b;
        System.out.println(lambdaFunction.max(3, 5));
    }

}

여기서 놓치지 말아야 하는 것은 람다식으로 생성된 순수 함수는 함수형 인터페이스로만 선언이 가능하다는 점이다. 또한 @FunctionalInterface는 해당 인터페이스가 1개의 함수만을 갖도록 제한하기 때문에, 여러 개의 함수를 선언하면 컴파일 에러가 발생한다.

이번에는 내가 썻던 코드 중 제대로 알지도 못한 채 무작정 썻던 람다식 하나를 다시 한번 살펴보겠다.

UserServiceException result= assertThrows(UserServiceException.class,()->
                                            basketService.basketToMyIngredient(userId,ingredName));

여기서 asserThrows 메서드의 원형을 보면,

public static <T extends Throwable> T assertThrows(Class<T> expectedType, Executable executable) {
		return AssertThrows.assertThrows(expectedType, executable);
	}

위와 같이 선언되어 있다.

그리고, assertThrows 메서드에서 람다식을 사용한 두번째 매개변수인 Executable 클래스의 원형을 살펴보면,

@FunctionalInterface
@API(status = STABLE, since = "5.0")
public interface Executable {

	void execute() throws Throwable;

}

위와같이 함수형 인터페이스가 선언되어 있는것을 확인할 수 있었다.
이를 확인 한후, 내가 썻던 코드를 다시 보니 assertThrows 메서드는 Exception 클래스와 함수형 인터페이스인 Executable 인터페이스를 매개변수를 가졌고, 두번째 매개변수는 함수형 인터페이스이기 때문에 람다식으로 직접 익명함수를 만들어주었던 것이다. 그리고 그 결과로 throw 된 Exception을 반환한다.

이렇게 람다식에 대해 정리해보았다.
완벽하게 이해된 것은 아니지만, 적어도 내가 쓰고있는 코드가 어떤 의미인지는 알고 사용할 수 있을 것 같다. 그리고, 위에서 람다식의 장점 중 병렬처리에 용이하다는 항목이 있었는데, 이는 Java8부터 지원하는 Stream과 관련된 내용이다. 이 부분은 또 다음에 다시 포스팅 하도록 하겠다.

참고자료

0개의 댓글