Study/Java

뒤늦게 공부해보는 함수형 인터페이스, 근데 이거 왜 씀?

dev_kong 2023. 3. 29. 15:57
728x90
728x90

우테코 레벨1 체스미션을 진행하며 함수형 인터페이스를 처음 사용해봤다.
정말 뒤늦게 공부하는 거 같긴한데.. 늦게라도 하는게 안하는 것보다는 항상 낫다고 생각한다.

함수형 인터페이스란?

함수형 인터페이스는 1개의 추상 메소드를 갖고 있는 인터페이스를 말한다.
다른 말로 SAM(Single Abstract Metehod)이라고 불리기도 한다.

public interface Example {  
   void doSomething(String string); // public abstract 생략
}

위의 인터페이스는 하나의 추상 메서드를 가지고 있는 인터페이스이다.
즉, 함수형 인터페이스의 조건을 충족하고있다.

 

위처럼 정의된 함수형 인터페이스는 람다를 이용해 접근할 수 있다.

 

Example func = (string) -> System.out.println(string);  
func.doSomeThing("Functional Interface!"); // Functional Interface! 출력

 

default method가 추가된다면?

java8 부터 interface에 default method 를 추가 할 수 있게 되었다.

 

public interface Example {  
   void doSomething(String string);  

    default void doSomeThing2() {  
        System.out.println("Hello Default");  
    }
}

 

위와 같이 default method를 포함하여 정의된 인터페이스 역시, 함수형 인터페이스다.
함수형 인터페이스의 정의인 단 하나의 추상메서드만을 포함하고 있는 인터페이스 이기 때문이다.

 

Example func = (string) -> System.out.println(string);  
func.doSomeThing("Functional Interface!"); // Functional Interface! 출력  
func.doSomeThing2(); // Hello Default 출력

 

마찬 가지로 람다식을 이용해 접근할 수 있고,
default로 정의된 method 또한 사용할 수 있다.

 

@FunctionalInterface annotation

위에서 정의한 Example이란 인터페이스를 함수형 인터페이스로 잘 사용하고 있었는데,
미래의 내가 해당 인터페이스가 함수형 인터페이스로 사용되고 있다는 것을 까먹거나,
나를 제외한 다른 인원이 해당 인터페이스가 함수형 인터페이스로 사용되고 있다는 사실을 인지하지 못하고,
새로운 추상메서드를 추가 하게 되는 경우가 생길 수도 있다.

 

public interface Example {  
   void doSomeThing(String string);  

   void doSomeThing2(String string);  
}

 

위와 같이 추상 메서드가 추가가 되면, 해당 인터페이스를 사용하던 곳에서 컴파일 에러가 발생한다.

 

Example func = (string) -> System.out.println(string); // 컴파일 에러 발생
func.doSomeThing("Functional Interface!");

 

이러한 케이스를 미연에 방지하고자 사용하는 것이 @FunctionalInterface 어노테이션이다.

함수형 인터페이스를 정의할 때, @FunctionalInterface 를 적용해주면 된다.

 

@FunctionalInterface  
public interface Example {  
   void doSomeThing(String string);  

   void doSomeThing2(String string);  
}

 

위와 같이 어노테이션을 적용 해주면, 해당 인터페이스가 함수형 인터페이스로 사용되고 있다는 것을 명시적으로 알 수 있다.
뿐만 아니라, 여러개의 추상메서드를 정의하게 되는 경우 컴파일 에러가 발생하여 위와 같은 케이스를 미연에 방지할 수 있다.

 

기본 함수형인터페이스

매번 이런식으로 함수형 인터페이스를 정의하는 것은 제법 귀찮은 일이다.


그런데 매우 고맙게도 java는 자주 쓰일법한 함수형인터페이스의 기본형들을 제공해주고 있다.

 

기본 함수형 인터페이스 같은 경우는 이미 너무 많은 정보가 있으므로 굳이 자세히 설명할 필요가 없을 듯 하다.

https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html
위의 링크에 java.util.function 에 정의된 함수형 인터페이스들의 목록이 주루룩 나와있으니,
필요한 거 찾아 쓰면 될 듯 하다.

 

java.util.function (Java Platform SE 8 )

Interface Summary  Interface Description BiConsumer Represents an operation that accepts two input arguments and returns no result. BiFunction Represents a function that accepts two arguments and produces a result. BinaryOperator Represents an operation u

docs.oracle.com

 

이걸 왜 쓰는거지?

공부하다 보니 든 생각인데, 굳이 굳이 어노테이션 까지 만들어 가며,

추상메서드가 하나여야한다는 까다롭다면 까다로운 조건을 만들어가면서 까지 함수형 인터페이스라는 개념을 만들어낸 이유가 무엇일까?

결론부터 얘기하자면, 람다식은 함수형 인터페이스를 반환하기 때문이다.


즉, 람다식을 쓰기 위함이다.

기존에도 익명 클래스를 써서 람다표현식을 사용하는 것과 동일한 결과를 얻어 낼 수 있었다.
(물론 람다와 익명클래스의 내부동작은 다르다.)

 

public interface Example {  
   void doSomeThing(String string);  
}

public void testMethod() {  
    Example func = new Example() {  
        @Override  
        public void doSomeThing(String string) {  
            System.out.println(string);  
        }    
    };    
    func.doSomeThing("Functional Interface!"); // Functional Interface! 출력  
}

 

그렇다면 왜 굳이 람다를 쓰기 위해 함수형 인터페이스 라는 새로운 개념을 적용한 것일까?

 

함수형 프로그래밍

이후부터는 뇌피셜이 가미된 글입니다.
피드백은 언제나 환영합니다.

결국은 패러다임이다. 함수형 패러다임이 대두 되기 시작하면서 자바에서도 함수형 프로그래밍을 지원하고자 했다.

함수형 프로그래밍을 모두 설명하자면 하루 왠종일걸릴 것 같으니, 가장 큰 특징만 설명하려한다.

 

함수형 프로그래밍이 가능하기위한 선결 조건이 있다.
함수가 일급 시민 이어야 한다.

 

일급시민의 정의는 다음과 같다.

  1. 변수에 함수를 할당 할 수 있어야 한다.
  2. 함수를 다른 함수에 인자로 넘길 수 있어야 한다.
  3. 함수에서 함수를 생성하여 반환할 수 있어야 한다.

자바의 함수는 일급시민일까?
'아니다.'라고 생각한다.

 

이거에 대해 얘기하면 너무 길어지기 때문에... 따로 포스팅 하기로 하고.

 

간략하게 함수형 프로그래밍과 함수형 인터페이스를 연결지어서 얘기해 보자면,
자바에서도 함수형 프로그래밍을 사용하게끔 하고 싶었고,
자바의 구조상 불가능 하지만, 가능한 것처럼 보이게끔 하기위해 함수형 인터페이스가 나왔다고 생각한다.

 

와 이거 돌맞을거 같은데....
근데 내 생각은 변함이 없다.

728x90
728x90