Java

[Java] 함수형 인터페이스, 람다, 메소드 레퍼런스에 대하여

Tommy__Kim 2024. 1. 15. 07:35

함수형 인터페이스, 람다, 메소드 레퍼런스 모두 긴밀한 연관을 가지고 있는 개념입니다. 

이번 글을 통해 해당 개념들을 살펴 보고자 합니다. 

함수형 인터페이스 

@FunctionalInterface
public interface Foo {
	int add (int a, int b);
    
    default void printDate(){
    	System.out.println(LocalDate.now());
    }
}

함수형 인터페이스란 추상 메소드를 단 하나 가지고 있는 인터페이스를 의미합니다.

default method, static method가 몇개 존재하던 추상 메소드를 단 하나만 가지고 있다면 함수형 인터페이스라고 합니다. 

Q) 추상 메소드란 ? 
A) 선언만 되어 있고, 구체적인 구현은 되어있지 않은 메소드입니다. 

Q) @FunctionalInterface 어노테이션은 반드시 붙여야 하나요?
 A) 아니요 반드시 붙일 필요는 없습니다. 다만 해당 어노테이션을 추가하게 된다면 추상 메서드가 두개 이상 존재할 때 컴파일 에러를 내주기 때문에 함수형 인터페이스를 의도하고 만든다면 어노테이션을 붙여주는 것이 좋습니다. 

자바에서 제공하는 함수형 인터페이스  (참고)

자바에서은 기본적으로 제공하는 함수형 인터페이스들이 있습니다.

수많은 인터페이스들을 제공하는데 핵심 인터페이스들만 안다면 나머지는 좀 더 편하게 이해 할 수 있습니다. 

Function<T,R>

Function<T,R>의 경우 T라는 타입의 매개변수를 받아서 R이라는 타입의 타입을 제공하는 함수형 인터페이스입니다.

Consumer<T>

Consumer<T>의 경우 T라는 타입의 매개변수를 받아 아무값도 return하지 않는 함수형 인터페이스입니다.

Supplier<T>

Supplier<T>의 경우 별다른 매개변수를 받지 않으며, T라는 타입의 값을 제공하는 함수형 인터페이스입니다.

Predicate<T>

Predicate<T>의 경우 T라는 타입의 매개변수를 받아 boolean을 return 하는 함수형 인터페이스입니다.

람다

람다 표현식은 메서드로 전달할 수 있는 익명 함수를 단순화한 것이라고 할 수 있습니다. 

람다 표현식에는 이름은 없지만, 파라미터 리스트, 바디, 반환 형식, 발생할 수 있는 예외 리스트는 가질 수 있습니다. 

람다 표현식은 보통 다음과 같이 구성됩니다.

(인자 리스트) -> {바디}

 

인자 리스트 

  • 인자가 하나도 없을 경우 : ()
  • 인자가 하나 존재하는 경우 : (a) 혹은 a
  • 인자가 여러개일 경우 : (a, b, c)

인자의 타입의 경우 컴파일러가 추론하기 때문에 생략이 가능합니다. 하지만 명시하고 싶은 경우 명시를 해도 됩니다.

바디

  • 바디에 여러 줄이 존재할 경우 {}로 묶어서 사용
  • 한줄인 경우에는 {} 생략 가능, return도 생략 가능

그 외 특징 

람다 캡처링(Capturing lambda)

public class Foo {
	private int baseNumber;
    
	void doSomething(){
		int baseNumber;
    
		IntConsumer printInt = (i) -> System.out.println(i + baseNumber);
	}
}

람다표현식의 경우 자유 변수 (파라미터로 넘겨진 변수가 아닌 외부에서 정의된 변수)를 사용할 수 있습니다. 

이 경우에 자유 변수는 final keyword로 명시된 변수이거나, effective final 이어야 합니다. 

final이 아닌 경우에는 concurrency 문제가 생길 수 있기 때문에 컴파일러가 이를 방지합니다. 

Q) effective final이란 무엇인가?
A) final 키워드가 붙진 않았지만 아무데서도 값을 변경하지 않아 사실상 final인 변수를 의미합니다.

메소드 레퍼런스 

람다가 하는 일이 기존 메소드 혹은 생성자를 호출하는 경우 메소드 레퍼런스를 사용해 비교적 간결하게 표현이 가능합니다. 

 

메소드 참조 방식

스태틱 메소드 참조 Type::staticMethod
특정 객체의 인스턴스 메소드 참조 객체 레퍼런스::instanceMethod
임의 객체의 인스턴스 메소드 참조 Type::instanceMethod
생성자 참조 Type::new