Java

[Java] Java 8의 특징(1) - 함수형 인터페이스와 람다

재담 2022. 2. 21. 23:53

함수형 인터페이스

함수형 인터페이스란 다음과 같이 추상 메서드를 딱 한 개만 가지고 있는 인터페이스를 의미한다.

@FunctionalInterface
public interface Animal {
    void move();
}

위 코드에서 @FunctionalInterface 어노테이션을 적용했는데 @FunctionalInterface 어노테이션이 없더라도 Animal 인터페이스는 함수형 인터페이스이다. 그럼 @FunctionalInterface 어노테이션의 역할은 무엇이냐? 만약 @FunctionalInterface 어노테이션을 선언하고 추상 메서드를 두 개 이상 선언할 경우 컴파일 에러가 발생한다.

 

자바에서 함수형 프로그래밍

함수형 프로그래밍이랑 객체의 형태를 벗어나 함수형을 확장해 함수를 변수처럼 사용 가능한 것을 의미한다. 자바에서 함수형 프로그래밍은 다음과 같은 특징들이 있다.

  • 함수를 First Class Object로 사용 가능 = 함수가 함수를 매개변수로 사용 가능, 함수를 리턴 가능(고차 함수)
  • 순수 함수(Pure Function)
    1. Side Effect가 없다.(함수 밖에 있는 값을 변경하지 않는다.)
    2. State가 없다.(함수 밖에 있는 값을 사용하지 않는다.)

 

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

자바 8에서 기본으로 제공하는 함수형 인터페이스에는 다음과 같은 것들이 있다.

 

Function<T, R>

  • T 타입을 받아서 R 타입을 리턴하는 함수형 인터페이스
    • R apply(T t)
  • 함수 조합용 메서드
    • andThen
    • compose

 

BiFuntion<T, U, R>

  • 두 개의 값(T, U)를 받아서 R 타입을 리턴하는 함수형 인터페이스
    • R apply(T t, U u)

 

Consumer<T>

  • T 타입을 받아서 아무 값도 리턴하지 않는 함수형 인터페이스
    • void Accect(T t)
  • 함수 조합용 메서드
    • andThen

 

Supplier<T>

  • T 타입의 값을 제공하는 함수형 인터페이스
    • T get()

 

Predicate<T>

  • T 타입을 받아서 boolean을 리턴하는 함수형 인터페이스
    • boolean test(T t)
  • 함수 조합용 메서드
    • And
    • Or
    • Negate

 

UnaryOperator<T>

  • Function<T, R>의 특수한 형태로, 입력값 하나를 받아서 동일한 타입을 리턴하는 함수형 인터페이스
  • == Funtion<T, T>

 

BinaryOperator<T>

  • BiFuntion<T, U, R>의 특수한 형태로, 동일한 타입의 입력값 두 개를 받아 동일한 타입을 리턴하는 함수형 인터페이스
  • == BiFuntion<T, T, T>

 

람다 표현식(Lambda Expressions)

자바 8에서 새롭게 등장한 람다 표현식은 함수형 인터페이스의 인스턴스를 간편하게 만드는 방법으로 쓰인다. 우선 다음 코드를 보자.

Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("New Thread");
    }
});

thread.start();

위 코드는 새로운 스레드를 생성할 때 많이 사용하는 코드이다. 람다 표현식을 사용하면 위 코드를 다음과 같이 간단하게 쓸 수 있다.

Thread thread = new Thread(() -> System.out.println("New Thread"));
        
thread.start();

람다는 기본적으로 다음과 같은 구조를 가진다.

  • (파라미터 리스트) -> {람다 바디}

 

파라미터 리스트는 다음과 같이 표현 가능하다.

  • 파라미터가 없을 때 : ()
  • 파라미터가 한 개일 때 : (param) 또는 param
    • 파라미터가 한 개일 때만 괄호를 생략 가능
  • 파라미터가 두 개 이상일 때 : (param1, param2)
  • 파라미터 타입은 생략 가능하지만 명시적으로 표현도 가능 : (Integer param1, String param2)

 

람다 바디는 다음과 같이 표현 가능하다.

  • 화살표(->) 오른쪽에 함수 본문을 정의
  • 한 줄인 경우에 중괄호를 생략 가능, return도 생략 가능
  • 여러 줄인 경우에는 중괄호와 return을 생략 불가능

 

람다 표현식에서 변수 캡처는 다음과 같은 규칙이 있다.

  • 로컬 변수 캡처
    • final이거나 effective final인 경우에만 참조 가능
    • 그렇지 않으면 컴파일 에러
  • effective final
    • 사실상 final인 변수
    • final로 선언하지 않았지만 선언 후 값을 변경하지 않는 변수
  • 익명 클래스는 새로 Scope을 만들지만, 람다는 람다를 감싸고 있는 Scope와 같다.

 

메서드 레퍼런스

람다가 하는 일이 기존 메서드 또는 생성자를 호출하는 거라면, 메서드 레퍼런스를 사용해서 다음과 같이 매우 간결하게 표현할 수 있다.

  • 스태틱 메서드 참조 : 타입::스태틱메서드
  • 특정 객체의 인스턴스 메서드 참조 : 객체::인스턴스메서드
  • 임의 객체의 인스턴스 메서드 참조 : 타입::인스턴스메서드
  • 생성자 참조 : 타입::new

메서드 또는 생성자의 매개변수로 람다의 입력값을 받고, 리턴 값 또는 생성한 객체는 람다의 리턴 값이다.


Reference