Java

[Java] 함수형 인터페이스(Functional Interface) - 매개변수, 형 변환, 변수 참조, function 패키지

Hyeonni 2022. 9. 15. 14:51

함수형 인터페이스는 람다식과 밀접한 관련이 있는 내용이다. 람다식을 잘 모르거나 다시 복습을 원한다면 아래 글을 참고하면 된다.

람다식(Lambda expression) - 생성 규칙

 

[Java] 람다식(Lambda expression) - 생성 규칙

자바가 등장한 이후로 자바가 더 이상 예전의 자바가 아니라는 말을 만들어낸 두 가지 변화가 있다. 바로 JDK1.5의 지네릭스(generics)의 등장이고, 다른 하나는 JDK 1.8(자바 8)부터 추가된 람다식이다

ta-mi.tistory.com

 

 

 

함수형 인터페이스(Functional Interface)란?

 

자바에서는 모든 메서드는 클래스 내에 포함되어야 한다. 그렇다면 람다식은 어떤 클래스에 포함이 되어 있는지 생각해 볼 필요가 있다. 람다식에 관한 글에서는 람다식을 메서드와 동등한 것처럼 다뤘는데, 사실 람다식은 익명 클래스의 객체와 동등하다.

 

그렇다면 람다식으로 정의된 익명 객체의 메서드를 호출하는 방법에 대해서도 알아야 람다식을 활용할 수 있을 것이다. 그동안 객체를 다뤄왔던 것처럼 참조 변수가 필요하다. 참조 변수의 타입은 클래스와 인터페이스가 가능하다. 그리고 람다식과 같은 메서드가 정의되어 있는 것이어야 참조 변수로 익명 객체의 메서드인 람다식을 호출할 수 있다. 

 

 

위 코드와 같이 MyFunction 익명 객체의 메서드 max 와 람다식의 매개변수 타입과 개수 그리고 반환 값이 일치하면 익명 객체를 람다식으로 대체가 가능하다. 실제로는 람다식도 익명 객체이기 때문이다. 이렇게 하나의 메서드가 선언된 인터페이스를 정의해서 람다식을 다루는 것은 기존의 자바의 규칙들을 어기지 않고 자연스럽다. 그래서 인터페이스를 통해 람다식을 다루기로 결정되었다.

 

람다식을 다루기 위한 인터페이스를 '함수형 인터페이스(functional interface)'라고 한다.

함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야만 한다. 그래야 람다식과 인터페이스의 메서드가 1:1로 매칭이 될 수 있기 때문이다. 반면 static 메서드와 default 메서드의 개수에는 제약이 없다.

'@FunctionalInterface'를 붙이면 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해주어 유용하다.

 

 

 

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

 

 

 

함수형 인터페이스가 1~4번 코드처럼 정의되어 있을 때, 7~9번 코드에서 함수형 인터페이스를 메서드의 매개변수로 선언되어 있다. 해당 의미는 해당 메서드를 호출할 때 람다식을 참조하는 참조 변수를 매개변수로 지정해야한다는 뜻이다. 참조변수를 선언하지 않고 바로 람다식을 넣어도 된다.

 

이런 식으로 메서드의 반환 타입이 함수형 인터페이스 타입이라면, 이 함수형 인터페이스의 추상 메서드와 동등한 람다식을 가리키는 참조 변수를 반환하거나 람다식을 직접 반환할 수 있다.

 

이렇게 람다식을 참조 변수로 다룰 수 있다는 것은 메서드를 통해 람다식을 주고받을 수 있다는 것을 의미한다. 즉, 변수처럼 메서드를 주고받는 것이 가능한 것이다. 사실상 메서드가 아니라 객체를 주고받는 것이므로 근본적으로 달라진 것은 없다.

 

 

 

람다식의 타입과 형 변환

 

함수형 인터페이스로 람다식을 참조할 수 있는 것일 뿐, 람다식의 타입이 함수형 인터페이스의 타입과 일치하는 것은 아니다. 람다식은 익명 객체이고 익명 객체는 타입이 없다. 정확히는 컴파일러가 임의로 타입을 지정해 알 수 없다. 그래서 대입 연산자의 양변의 타입을 일치시키기 위해 형 변환이 필요하다. 

주의할 점은 람다식은 Object 타입으로 바로 형변환이 안되기 때문에 함수형 인터페이스로 형 변환을 한 다음 Object 타입으로 형변환을 해야 한다. 

 

 

 

람다식의 변수 참조

 

람다식 내에서 참조하는 지역변수는 final이 붙지 않았어도 상수로 간주된다. 반면 외부 클래스의 변수 참조는 값 변경이 가능하다. 그리고 외부 지역변수와 같은 이름의 람다식 매개변수는 허용되지 않는다.

 

 

 

java.util.function 패키지

 

함수형 인터페이스를 따로 정의하지 않고 사용할 수 있도록 미리 정의해둔 인터페이스들이 있다. 이 패키지의 인터페이스를 사용하면 함수형 인터페이스에 정의된 메서드 이름도 통일되고, 재사용성이나 유지보수 측면에서도 좋다.

 

java.util.function 패키지의 주요 함수형 인터페이스 (타입 문자 T는 type을 R은 Return Type을 의미한다.)

함수형 인터페이스 메서드 설명
java.lang.Runnable void run() 매개변수도 없고 반환값도 없다.
Supplier<T> T get() 매개변수는 없고, 반환값만 있다.
Consumer<T> void accept(T t) Supplier와 반대로 매개변수만 있고,
반환값이 없다.
Function<T, R> R apply(T t) 일반적인 함수, 하나의 매개변수를 받아서 결과를 반환한다.
Predicate<T> boolean test(T t) 조건식을 표현하는데 사용한다.
매개변수는 하나, 반환 타입은 boolean

 

 

 

조건식이 표현에 사용되는 Predicate

 

함수형 인터페이스 Predicate<T>는 boolean test(T t) 메서드를 가지고 있다. 조건식을 표현하는데 사용되며, 매개변수는 하나를 받고, 반환 타입은 boolean이다.

 

 

매개변수가 두 개인 함수형 인터페이스

 

매개변수의 개수가 2개인 함수형 인터페이스는 이름 앞에 접두사 'BI'가 붙는다.

세 개 이상의 매개변수를 갖는 함수형 인터페이스가 필요하다면 직접 만들어서 사용해야 한다.

 

함수형 인터페이스 메서드 설명
BiConsumer<T, U> void accept(T t, U u) 두개의 매개변수만 있고, 반환값이 없다.
BiPredicate<T, U> boolean test(T t, U u) 조건식을 표현하는데 사용된다.
매개변수는 둘, 반환값은 boolean이다.
BiFunction(T, U, R) R apply(T t, U u) 두개의 매개변수를 받아서 하나의 결과를 반환한다.

 

Function의 또 다른 변형

함수형 인터페이스 메서드 설명
UnaryOperator<T> R apply(T t) Function의 자손, Function과 달리 매개변수와 결과의 타입이 같다.
BinaryOperator<T> R apply(T t, T t) BiFunction의 자손, BiFunction과 달리
매개변수와 결과의 타입이 같다.

매개변수의 타입과 반환타입이 일치할 때는 Function대신 UnaryOperator를 사용하는 것이 좋다.

 

 

기본형을 사용하는 함수형 인터페이스

 

함수형 인터페이스 메서드 설명
DoubleToIntFunction int applyAsInt(double d) AToBFunction은 입력이 A타입 출력이 B타입임을 의미한다.
ToIntFunction<T> int applyAsInt(T value) ToBFunction은 출력이 B타입이고 입력은 지네릭 타입이다.
IntFunction<R> R apply(int value) AFunction은 입력이 A타입이고 출력은 지네릭 타입이다.
ObjIntCunsumer<T> void accept(T t, int i) ObjAFunction은 입력이 T, int 타입이고 출력은 없다.

 

 

 


자바의 정석(남궁성)을 정리한 글 입니다.