호우동의 개발일지

Today :

article thumbnail

함수형 인터페이스를 알면 편한 이유

우리가 보통 코딩을 할 때, Visual Studio Code, Intellij 등 IDE 환경에서 진행한다.
IDE를 사용하면 엄청나게 편리하다.

내가 여기서 말하고자 하는 것은, IDE가 클래스의 함수 이름과 기능을 제공해 주는 것에 대한 것이다.
컬렉션이 어떤 함수를 가지고 있는지를 알 수 있고, 어떤 기능을 하는지 알 수 있다.

흔히 볼 수 있는 함수형 인터페이스
흔히 볼 수 있는 함수형 인터페이스

위는 인텔리제이에서 arrayList 컬렉션에서
사용할 수 있는 함수를 IDE에서 창으로 알려주는 모습이다.

빨간 줄 부분이 전부 함수형 인터페이스 부분이다.
사실 함수형 인터페이스는 생소하지만 항상 우리 옆에 있었다.

함수형 인터페이스를 모른다면, IDE에서 해당 함수의 사용법을 알려줬는데도 사용하지 못하는 것이다.

예를 들어서 stream(). map(Function mapper)에서 매개변수로 Function이 들어간다.

함수형 인터페이스를 모르는 나는 어떻게 해야 하는지 몰라 인터넷을 켜고 예제를 찾아본다

하지만 함수형 인터페이스에 대해 이해하고 있다면 이런 시간을 아낄 수 있다.

 

 


함수형 인터페이스의 정의

추상 메서드를 딱 하나만 가지고 있는 인터페이스
@FunctionalInterface
public interface MyInterface{
    int method1(); // 추상 메서드
}

여기서 myInterface는 method1이라는 추상 메서드만 가지고 있다.

가장 위에 @FunctionalInterface는 함수형 인터페이스를 의미하는 어노테이션인데,

이를 사용하여 코드 작성 에러를 방지할 수 있다.

추상 메서드를 하나 더 입력하자, 에러가 발생한다.
추상 메서드를 하나 더 입력하자마자, 경고 발생한다.

위처럼 method2()라는 추상메서드를 하나 더 입력하자,
FunctionInterface에 경고가 발생하는 것을 확인할 수 있다.

여기서 중요한 것은 추상 메서드의 개수가 딱 하나여야 한다는 것이다.

@FunctionalInterface
interface MyInterface{
    int method1();

    static int method2(){
        return 0;
    }
}

 위 함수는 메서드를 2개를 가짐에도 함수형 인터페이스이다.


method2는 static 메서드이기 때문에 추상 메서드에 속하지 않는다.
즉, 여전히 추상 메서드는 딱 1개이다. 따라서 함수형 인터페이스이다.

 

 


함수형 인터페이스의 사용

일반적인 인터페이스 사용과 비교해 보자.

MyInterface myInterface1 = new MyInterface() {
            @Override
            public void method1() {
                System.out.println("일반적인 인터페이스 호출");
            }
        };
        
        
 MyInterface myInterface2 = () -> System.out.println("함수형 인터페이스 호출");
 
 myInterface1.method1(); // 일반적인 인터페이스 호출
 myInterface2.method1(); // 함수형 인터페이스 호출

일반적인 인터페이스는 method1을 오버라이딩한 뒤, 내부에 코드를 적어 구현한다.

하지만 함수형 인터페이스를 사용하면 람다식을 이용하여 더 간략하게 표현할 수 있다.

물론 매개변수와 리턴값이 있는 추상 메서드도
람다식으로 표현 가능하다.

@FunctionalInterface
interface MyInterface{
    int method1(int a, int b);
}

public static void main(String[] args) {
    MyInterface myInterface = (a,b) -> {return a+b;};

    System.out.println(myInterface.method1(5,2)); // 7
}

 


함수형 인터페이스가 매개변수로 사용될 때

이제부터가 본론이다.

함수형 인터페이스가 매개변수로 사용되는 경우,
즉 가장 위에 있는 그림의 상황에서는 어떻게 사용해야 할까?

@FunctionalInterface
interface MyInterface{
    int method1(int a, int b);
}

// 함수형 인터페이스 MyInterface를 매개변수로 지님
static int test(MyInterface myInterface, int num1, int num2){
    return myInterface.method1(num1,num2);
}

public static void main(String[] args) {
	// method1의 구현 내용을 람다식으로 곧바로 구현
    int result = test((a,b) -> {return a*b;},4,5);
    System.out.println(result); // 20
}

위처럼 함수형 매개변수에게 할당되는 곳에
곧바로 method1의 구현체에 대한 람다식을 적으면 된다.

 

 


자바에서 함수형 인터페이스를 미리 제공해 준다.

위 글만 잘 이해했다면,
이제 함수형 인터페이스가 매개변수로 들어오더라도 충분히 사용할 수 있을 것이다.

이제 남은 것은 자바에서 미리 제공해 주는
함수형 인터페이스 유형에 대해 아는 것만 하면 된다.

자주 사용하는 함수형 인터페이스를 표로 정리해 봤다.

대표적인 함수형 인터페이스 표준 AP
대표적인 함수형 인터페이스 표준 AP

기본적인 형태는 위의 6가지이다. 여기서 다른 표준형 인터페이스들이 파생된다.

예를 들어 BiFunction <T, U, R> 은
매개변수를 2개 받고 R을 반환하는 것을 의미한다.

이외에도 XXXConsumer는 XXX의 자료형을 매개변수로 사용한다는 것을 의미한다.

이 외에도 많은 파생이 있기 때문에 나중에 따로 정리하겠다.

 

 


적용해 보기

여태까지 이해했던 것을 적용할 수 있는 가장 좋은 곳은 Stream이다.

map은 Function&lt;T&#44;R&gt;을 매개변수로 가짐
map은 Function<T,R>을 매개변수로 가진다.
Consumer을 매개변수로 가진다.
Consumer을 매개변수로 가진다.

이 2개를 이용해서 이런 식으로 쓸 수 있다.

public static void main(String[] args) {
    List<Integer> list = List.of(1,2,3,4,5,6);
    list.stream().map(e -> String.valueOf(e)).forEach(e -> System.out.print(e));
    
    // 출력 : 123456
}


이렇게 자바 함수형 인터페이스의 표준 API를 잘 이해하고 있다면,
검색까지 갈 필요 없이 IDE 선에서 쉽게 해결할 수 있다.

사실 내가 이거 알아내서 공유하려고 글 썼다.

참고 블로그 

https://catsbi.oopy.io/e980 e4 b7-fde3-4 ceb-91f9-181 ce2e7b507

https://inpa.tistory.com/entry/☕-함수형-인터페이스-API#

https://catsbi.oopy.io/e980 e4 b7-fde3-4 ceb-91f9-181 ce2e7b507