프로그래밍/JAVA Spring

[JAVA 자바] 람다(lambda)

hectick 2023. 3. 11. 09:53

 

🐣 람다식

 

람다식(Lambda expression)은 간단히 말해서 메서드를 하나의 '식(expression)'으로 표현한 것이다. 메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 사라지게 되는데, 그래서 람다식은 익명 함수라고도 한다.

람다식은 메서드의 매개변수로 전달되어지는 것이 가능하고, 메서드의 결과로 반환될 수도 있다. 즉, 람다식으로 인해 메서드를 변수처럼 다룰 수 있다는 뜻이다.

 

  • 보통의 메서드와 달리 이름이 없으므로 익명이라 표현한다.
  • 람다는 메서드처럼 특정 클래스에 종속되지 않으므로 함수라고 부른다.
  • 람다식을 메서드 인수로 전달하거나 변수로 저장할 수 있다.
  • 익명 클래스처럼 많은 자질구레한 코드를 구현할 필요가 없기 때문에 간결하다.

 

람다식은 파라미터, 화살표, 바디 세 부분으로 이루어진다.

	(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight())

화살표(->)를 기준으로 왼쪽이 람다 파라미터 리스트이고, 오른쪽이 람다 바디이다.

 

파라미터의 타입은 대부분의 경우에 생략 가능하고,  파라미터가 하나뿐인 경우에는 괄호()를 생략 할 수 있다.

단, 매개변수의 타입이 있으면 괄호를 생략할 수 없다.

	a -> a * a // OK
	int a -> a * a // 에러

 

바디도 문장이 하나일 때는 괄호{}를 생략할 수 있다. 이 때 문장의 끝에 ;를 붙이지 말아야 한다.

단, return문일 경우엔 괄호{}를 생략할 수 없다.

	(int a, int b) -> {return a > b ? a : b; } // OK
	(int a, int b) -> return a > b ? a : b // 에러

 

다음 람다식 인텔리제이에서 다음 람다식을 ctrl+alt+v로 변수를 추출해보면 어떻게 될까?

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

 

참조변수가 추출되고, 이 변수의 타입은 인터페이스가 된다. 그리고 @FunctionalInterface가 붙어있는 것도 볼 수 있다. @FunctionalInterface는 함수형 인터페이스임을 가리키는 어노테이션이다. 여기서 람다식을 다루기 위해서는 함수형 인터페이스가 필요함을 유추해 볼 수 있다.

 

 

🐣 람다식을 사용하는 법: 함수형 인터페이스

 

함수형 인터페이스?

람다식을 다루기 위한 인터페이스를 함수형 인터페이스라고 한다. 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야 한다. 그래야 람다식과 인터페이스의 메서드가 일대일로 연결될 수 있기 때문이다. default, static 메서드가 여러개 있어도가 있더라도 추상 메서드가 오직 하나이기만 하면 함수형 인터페이스다. 

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

 

함수형 인터페이스로 뭘 할수 있을까?

람다식으로 함수형 인터페이스의 추상 메서드 구현을 직접 전달할 수 있다. 즉, 람다식이 메서드의 매개변수로 전달되어지는 것이 가능하고, 메서드의 결과로 반환될 수도 있다.

 

다음 예제는 메서드의 매개변수의 타입이 함수형 인터페이스인 경우이다. 이 메서드를 호출 할 때, 람다식을 참조하는 참조변수를 매개변수로 지정해야 한다는 뜻이다.

	void aMethod(MyFunction f) {
		f.max(5,3);
	}

	MyFunction f = (int a, int b) -> a > b ? a : b;
	aMethod(f);

 

참조변수 없이 다음과 같이 직접 람다식을 매개변수로 지정할 수도 있다.

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

 

 

다음 예제는 메서드의 반환타입이 함수형 인터페이스인 경우이다. 여기에서도 함수형 인터페이스의 추상메서드와 동등한 람다식을 가리키는 참조변수를 반환하거나, 람다식을 직접 반환할 수 있다.

	MyFunction myMethod() {
		MyFunction f = (int a, int b) -> a < b ? b : a;
		return f; // 이 줄과 윗 줄을 한 줄로 줄이면 return (int a, int b) -> a < b ? b : a;
	}

 

 

🐣 @FunctionalInterface

 

@FunctionalInterface는 함수형 인터페이스임을 가리키는 어노테이션이다. @FunctionalInterface로 인터페이스를 선언했지만 실제로 함수형 인터페이스가 아니면 컴파일러가 에러를 발생시킨다. 예를 들어 추상 메서드가 한 개 이상이라면 "Multiple nonoverriding abstract mehthods found in interface Foo(인터페이스 Foo에 오버라이드 하지 않은 여러 추상 메서드가 있음)" 같은 에러가 발생할 수 있다.

 

 

🐣 기본 제공되는 함수형 인터페이스

 

하지만 람다식을 사용할 때마다 함수형 인터페이스를 매번 정의하기에는 불편하다. 그래서 자바에선 가장 기본적인 함수형 인터페이스를 제공해준다. 아래의 것 이외에도 java.util.function 패키지에 정의되어 있다.

 

함수형 인터페이스 매개변수 메서드 반환값
java.lang.Runnable X void run() X
Supplier<T> X T get() O (T)
Consumer<T> O (T) void accept(T t) X
Function<T,R> O (T) R apply(T t) O (R)
Predicate<T> O (T) boolean test(T t) O (boolean)

 

다음은 코드는 Predicate<T> 인데, 여러개의 Predicate를 and(), or(), negate()로 연결하면 하나의 새로운 Predicate로 결합할 수 있다.

public interface Predicate<T> {
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

 

 

🐣 메서드 참조

 

람다식이 하나의 메서드만 호출하는 경우에는 메서드 참조(method reference)라는 방법으로 람다식을 간략히 할 수도 있다. 인텔리제이에서는 람다식을 적으면 알아서 메서드 참조가 더 나은 방법이라며 바꿔주는 기능이 있다.

(String s) -> Integer.parseInt(s); //이것은 Integer::parseInt와 같다.

 

 

종류 람다 메서드 참조
static메서드(클래스메서드) 참조 (x) → ClassName.method(x) ClassName::method
인스턴스메서드 참조 (obj, x) → obj.method(x) ClassName::method
특정 객체 인스턴스메서드 참조 (x) → obj.method(x) obj::method

 

즉, 하나의 메서드만 호출하는 람다식은 ‘클래스 이름::매서드 이름’ 또는 ‘참조변수::메서드 이름’으로 바꿀 수 있다.

 

 

 


혹시 내용에 오류가 있다면 댓글로 알려주시면 감사하겠습니다.