[Java] Java 8의 특징(1) - 함수형 인터페이스와 람다
함수형 인터페이스
함수형 인터페이스란 다음과 같이 추상 메서드를 딱 한 개만 가지고 있는 인터페이스를 의미한다.
@FunctionalInterface
public interface Animal {
void move();
}
위 코드에서 @FunctionalInterface 어노테이션을 적용했는데 @FunctionalInterface 어노테이션이 없더라도 Animal 인터페이스는 함수형 인터페이스이다. 그럼 @FunctionalInterface 어노테이션의 역할은 무엇이냐? 만약 @FunctionalInterface 어노테이션을 선언하고 추상 메서드를 두 개 이상 선언할 경우 컴파일 에러가 발생한다.
자바에서 함수형 프로그래밍
함수형 프로그래밍이랑 객체의 형태를 벗어나 함수형을 확장해 함수를 변수처럼 사용 가능한 것을 의미한다. 자바에서 함수형 프로그래밍은 다음과 같은 특징들이 있다.
- 함수를 First Class Object로 사용 가능 = 함수가 함수를 매개변수로 사용 가능, 함수를 리턴 가능(고차 함수)
- 순수 함수(Pure Function)
- Side Effect가 없다.(함수 밖에 있는 값을 변경하지 않는다.)
- 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
메서드 또는 생성자의 매개변수로 람다의 입력값을 받고, 리턴 값 또는 생성한 객체는 람다의 리턴 값이다.