Monday

[Java8] 함수형 인터페이스란? 본문

언어/Java

[Java8] 함수형 인터페이스란?

뉴비2 2022. 2. 27. 20:38

1. 함수형 인터페이스란?

함수형 인터페이스란 "오직 하나의 추상 메소드"를 갖는 인터페이스를 말합니다. 즉, 인터페이스 안에 1개의 추상메소드만 있으면 함수형 인터페이스라고 부를 수 있습니다. 예를 들어, 인터페이스 안에 자바8에 추가된 디폴트 메서드가 있더라도 추상 메소드가 1개면 함수형 인터페이스입니다. 그리고 함수형 인터페이스를 선언 할 때 @FunctionalInterface 어노테이션을 붙여주면 컴파일러가 올바르게 정의되어 있는지 확인해주므로 꼭 붙여주는 게 좋습니다.

// 함수형 인터페이스 예제 1 - 추상 메소드가 1개
public interface Adder{
	int add(int a, int b);
}

// 함수형 인터페이스 예제 2 - 디폴트 메소드가 있지만, 추상 메소드가 1개
@FunctionalInterface
public interface Calculator{
    int add(int a, int b);
    default int sub(int a, int b){
        return a-b;
    }
}

2. 함수형 인터페이스를 쓰는 이유?

함수형 인터페이스 사용 이유를 설명하기 위해서는 "람다식"을 얘기해야 합니다. 람다식이란 "(파라미터) -> {구현바디}" 꼴로 함수형 인터페이스의 인스턴스로 취급이 가능합니다. 요약하면, 함수형 인터페이스라면(=추상화 메소드 1개를 갖는 인터페이스라면) 람다식으로 인스턴스화 할 수 있고, 이는 코드의 축약을 가져올 수 있습니다. 알기 쉽게 코드를 통해 살펴보겠습니다.

//함수형 인터페이스 정의
@FunctionalInterface
public interface Adder{
    int add(int a, int b);
}

//함수형 인터페이스 인스턴스화 예제
public class App 
{
    public static void main( String[] args )
    {
    	//익명 클래스로 함수형 인터페이스를 인스턴스화
        Adder adder1 = new Adder(){
            @Override
            public int add(int a, int b){
                return a+b;
            }
        };

		//람다식으로 함수형 인터페이스를 인스턴스화
        Adder adder2 = (a, b) -> a+b;

		// 실행 결과가 같다.
        System.out.println(adder1.add(1,3));
        System.out.println(adder2.add(1,3));
    }   
}

3. 자바에서 제공하는 함수형 인터페이스

함수형 인터페이스 메소드 사용 시기
Predicate<T> boolean test(T t) T 형식 인자 1개를 받아 true, false 타입으로 반환해야하는 경우
Consumer<T> void accept(T t) T 형식 인자를 받고 반환 없이 어떤 동작을 수행하고 끝낼 경우
Supplier<T> T get() 인자를 받지 않고 T 형식 객체를 반환 할 때 경우. 예를 들어,
() -> new Adder() 처럼 생성자 형식일 때 사용합니다.
Function<T, R> R apply(T t) T 형식 인자를 받아 R 형식을 반환 할 때 경우
Comparator<T> int compare(T o1, T o2) T 형식 인자를 받아 int 타입을 반환 할 경우
Runnable void run() 아무런 인자도 받지 않고 반환도 하지 않습니다. 보통 스레드와 사용 
Callable<V> V call() Supplier와 비슷하나, 만들어진 용도가 조금 다릅니다. Callable은 Runnable과 병렬처리에 자주 사용됩니다.

4. 기본형 특화 인터페이스

지금까지 확인한 함수형 인터페이스를 제네릭 함수형 인터페이스라고 합니다. 예를 들어, Consumer<T> 에서 T 는 제네릭 타입으로 참조형만 사용 가능합니다.

  • 참조형 (Reference Type) : Byte, Integer, Object, List
  • 기본형 (Primitive Type) : int, double, byte, char

지금까지 학습한 함수형 인터페이스를 사용하기 위해서는 기본형의 경우 참조형으로 변환 후에 사용할 수 있습니다. 이런 변환 과정은 기본형을 감싸는 래퍼를 생성하고 힙에 저장합니다. 즉, 메모리를 더 소비하게 되고 가져올 때도 메모리를 탐색하는 과정이 필요합니다. 

  • 박싱 (Boxing) : 기본형 -> 참조형 (int -> Integer)
  • 언박싱 (Unboxing) : 참조형 -> 기본형 (Integer -> int)
  • 오토 박싱(AutoBoxing) : 개발자가 박싱, 언박싱을 신경쓰지 않고 개발할 수 있게 자동을 변환해주는 기능입니다. 예를 들어, List<Integer> list에 list.add(1)이 가능한 이유는 기본형을 참조형으로 오토박싱 해주기 때문입니다.

함수형 인터페이스에서는 이런 오토박싱 동작을 피할 수 있도록 기본형 특화 함수형 인터페이스 를 제공합니다. 일반적으로 특정 형식을 입력으로 받는 함수형 인터페이스 이름 앞에는 IntPredicate, LongPredicate처럼 형식명이 붙습니다.

IntPredicate, LongPredicate 등등 특정 타입만 받는 것이 확실하다면 기본형 특화 인터페이스를 사용하는 것이 더 좋습니다.

'언어 > Java' 카테고리의 다른 글

2. Java 기본 문법(데이터 타입, 변수, 제어문)  (0) 2024.11.10
1. 실무에서 자주 사용하는 Java 코딩 컨벤션  (0) 2024.11.10
[Java8] Optional 이란?  (0) 2022.04.15
[Java8] Stream이란?  (0) 2022.03.13
Java8 Unsigned  (0) 2021.08.01
Comments