Skip to content

juneyr.dev

자바 8의 함수형 인터페이스는 뭐에요

Java, Lambda2 min read

자바! 학교에서도 지속적으로 배우는 가장 익숙한 프로그래밍 언어 중 하나이다. 졸업때까지(2018) 만 해도 학부에서 파이썬이나 go 같은 트렌디한 언어가 아니라 자바를 한다고 하면 ??? 하고 바라보고, 고루한 언어처럼 여겨지기도 했다.

자바를 제대로 처음 배울 때는 2014년이었는데, 사실 이때 Java8이 릴리즈됐다. 그러나 그때의 자바 강의는 자바의 OOP 로서의 부분에 집중해서 배웠고, 클래스, 인스턴스, 상속 ... 등의 개념 등만 줄을 이었다. 붕어빵 틀과 붕어빵 예제를 몇번이나 들었는지.

변하지 않고 내는 OOP임! 할 거 같던 자바는 Java 8로 엄청난 변신을 시도했다. Optional, Stream, Lambda Expression .. 즉 함수형 프로그래밍 패러다임을 도입한 것이다. 애초에 설계에 포함되어 있지 않은 패러다임을 어떻게 도입하냐고? 함수 자료형을 가져오는 과정을 객체로 바꿔버렸다.

아니 일단 함수형 프로그래밍이 뭐야ㅠㅠ 쉽게 좀..

일반적으로 어떤 이론의 앞에 있는 말이 그 이론을 설명하고, 가장 중요한 개념이다. 즉, 함수형 프로그래밍에서도 제일 중요한 건 함수 이다. 얼마나 중요하냐면, 함수형 프로그래밍은 함수를 1급 시민 으로 처리한다 !

..그런데, 1급 시민이 뭐야?

1급 시민은 변수에 담을 수 있고, 인자로 전달할 수 있으며, 반환값으로 전달할 수 있는 것을 의미한다. 프로그래밍 언어 입장에서는 아주 최선으로 생각한다 = 변수도 되고 인자도 되고 리턴 값도 된다 ! 라는 뜻이다.

그래서, 함수형 프로그래밍은 함수를 변수에 담고, 인자로 전달하고, 반환 값으로 전달할 수 있는 프로그래밍 방식을 의미한다. 이렇게 하면, 고차함수 를 만들어서 특정 함수의 작업 내용과 결과를 2차, 3차로 고도화할 수 있다는 장점이 나온다.

아래 (함수형 프로그래밍언어인) 자바스크립트 예제를 보자.

함수를 인자로 쓸 수 있고, 반환 값으로 쓸 수 있기때문에 귀찮게 method() 의 결과를 다른 변수에 담아줄 필요 없이 고차 함수를 완성했다.

OOP이던 자바 입장에서는 그동안 객체가 애지중지하던 1급시민이다. 하지만 자바 8부터는 함수형 프로그래밍의 컨셉을 가지고 오려고 람다식을 도입하게 되며, 이를 모던 자바 라고 한다.

람다..? λ..? 파장 아니니..?

람다(Lambda)는 익명함수를 의미한다. 이름 없이 간결하게 함수를 작성할 수 있다. 핵심 역시 지울 수 있는 건 모두 지우자는 것이다.

추상 메소드를 하나만 가진 Person 인터페이스를 구현하는 아래 예제를 보자.

인터페이스 한번 구현하는데 너무 줄을 많이 차지한다. 이걸 고치고 싶다.

  • new Person()은 앞에서 Person을 선언해서 알 수 있다.
  • 정작 구체적으로 선언해야 할 건 talk 메소드 뿐인데, 하나밖에 없으면 이름 생략해도 되지 않을까?
  • 인자(word)도 순서대로 넣으면 추론 가능하지 않을까?

이렇게 탄생한 게 아래다.

  • word 하나뿐이고, 아래도 프린트문 하나뿐이니 괄호는 지워보자.

    Person person = word -> System.out.println(word + "라고 생각합니다.");

코드가 매우 많이 줄어들었다. 이것만으로도 충분한 이점이 보인다 👻

함수형 인터페이스

슬쩍 말했지만, 위에서는 추상 메소드가 하나인 인터페이스를 람다로 바꾸었다. 그럼 추상 메소드가 두개면? 가차없이 안된다. 그러니까, 람다로 쉽게 만들고 싶어서 일부러 메소드를 하나만 만들었는데 다른 사람이 맘대로 추가하는 경우는 없어야한다. 그래서 아예 이런 인터페이스들에게 이름을 붙여주었다. 이게 바로 함수형 인터페이스 이다.

@FunctionalInterface 를 붙이면 해당 인터페이스가 함수형 인터페이스임을 알려준다.

그리고도 자바가 기본적으로 제공하는 함수형 인터페이스들이 있다.

자주 나오고 자주 사용되는 인터페이스들 이니 한번씩 꼭 보도록 하자 =)

Runnable

스레드 생성 시 자주 사용되는 인터페이스이며, 기존부터 존재하던 인터페이스이다.

Supplier<T>

인자가 없이, T라는 타입을 리턴하는 메서드 get 을 갖고 있다.

인자가 없는데도 T를 제공(Supply) 하는 인터페이스.

Consumer<T>

T라는 타입의 인자를 받고, 리턴을 하지 않는 메서드 accept을 갖고 있다.

인자를 먹어(Consume) 버리는 인터페이스.

Function<T,R>

T 타입을 받아 R 을 리턴하는 기본적인 함수형. 메서드 apply를 갖고 있다.

Predicate<T>

T타입을 받아 boolean을 리턴하는 인터페이스. Function에서 R이 boolean으로 고정된 것이라고 보면 된다.

test라는 메소드를 갖고 있다.

Predicate이 전제 or 명제라는 뜻이므로 당연히 boolean 값을 리턴할 것 같다. (?)

UnaryOperator<T>

Una- 는 하나라는 뜻이다(스페인어에서도 uno는 1 이니께)

Function의 특이한 종류인지, 메서드를 똑같이 apply 를 사용한다.

인자를 T타입으로 받아 T타입을 리턴한다.

String을 받아 String을 리턴하는 식!

BinaryOperator<T>

동일한 타입의 인자 두개가 들어가서 동일한 타입의 리턴 타입 하나가 나온다.

2 → 인터페이스 → 1 !

BiPredicate<T,U>

Predicate인데, 서로 다른 인자 두개를 받아서 boolean을 준다. Type은 같아도 달라도 상관없다.

BiConsumer<T,U>

Consumer인데, 서로 다른 인자 두개를 받아서 먹어버린다. Type은 같아도 달라도 상관없다.

BiFunction<T,U,R>

앞에 두 개(T,U) 인자를 받아서 → R 타입으로 반환한다.

Comparator<T>

Runnable 처럼 기존에 있던 자바 인터페이스. 객체간의 우선순위를 비교할 때 사용한다.

람다의 출현으로 더욱 간결해짐!

이 정도로 자바 8의 함수형 인터페이스와 람다 에 대해 알아보고, 메소드 레퍼런스를 비롯한 다른 특징은

다른 포스트에서 정리하기로 한다.


참고

https://brunch.co.kr/@kd4/12

http://multifrontgarden.tistory.com/124?category=471239

http://multifrontgarden.tistory.com/125

https://www.baeldung.com/java-8-new-features