1. 함수형 인터페이스
- 함수형 인터페이스는 오직 하나의 추상 메서드만을 지정하는 인터페이스이다.
- 이 때, 디폴트 메서드, 스태틱 메서드의 포함 여부는 상관없다.
- 람다 표현식을 통해 함수형 인터페이스의 추상 메서드 구현을 전달할 수 있다. 그러므로 해당 표현식을 함수형 인터페이스의 인스턴스로 취급할 수 있다.
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@FunctionalInterface
애노테이션을 통해 함수형 인터페이스임을 컴파일러에 알려줄 수 있다. 만일 두 개 이상의 함수형 인터페이스를 선언한다면 컴파일 에러가 발생한다.
1.1 익명 클래스
- 자바는 클래스 선언과 인스턴스화를 동시에 수행할 수 있도록 익명 클래스라는 기법을 제공한다.
- 말 그대로 이름이 없는 클래스이다. 이를 통해 필요한 구현을 즉석에서 만들어 사용할 수 있다.
public class Main {
public static void main(String[] args) {
Runnable hello = new Runnable() {
@Override
public void run() {
System.out.println("Hello World");
}
};
hello.run();
}
}
2. 람다 표현식
- 익명 클래스가 이름이 없는 클래스였다면, 람다 표현식은 이름이 없는 익명 함수라고 할 수 있다.
- 이름은 없으나 파라미터 리스트, 바디, 반환 타입, 발생할 수 있는 예외 리스트를 가질 수 있다.
람다는 세 부분으로 이루어진다.
(Integer number) -> String.valueOf(number);
파라미터 리스트
- String.valueOf() 메서드의 파라미터
- 화살표*
- 람다의 파라미터 리스트와 바디를 구분
- 바디*
- Integer 타입의 number를 받아서 문자열로 반환해준다.
람다는 문법에 따라 표현식 스타일과 블록 스타일 두 가지로 나뉜다.
(parameters) -> expression
(parameters) -> {
statements;
}
람다 표현식을 이용하여 함수형 인터페이스의 간결한 구현이 가능하다.
public class Main {
public static void main(String[] args) {
Runnable hello = () -> System.out.println("Hello World");
hello.run();
}
}
2.1 다양한 함수형 인터페이스
java.util.function 패키지는 여러 가지 함수형 인터페이스를 제공한다.
Predicate
- test() 라는 추상 메서드를 정의하며 제네릭 형식 T 타입 객체를 인수로 받아 boolean을 반환한다.
@FunctionalInterface public interface Predicate<T> { boolean test(T t); }
Consumer
- 제네릭 형식 T 타입 객체를 받아 void를 반환하는 추상 메서드 accept() 를 정의한다.
- T 타입의 객체를 가지고 어떠한 동작을 수행하고 할 때 사용할 수 있다.
@FunctionalInterface public interface Consumer<T> { void accept(T t); }
Supplier
- T 타입의 객체를 반환하는 추상 메서드 get() 을 정의한다.
- 이름 그대로 어떠한 값의 공급자 역할을 수행한다.
@FunctionalInterface public interface Supplier<T> { T get(); }
Function
- 제네릭 형식 T 타입 객체를 받아 R 타입 객체를 반환하는 추상 메서드 apply() 를 정의한다.
- 어떠한 입력을 다른 값으로 매핑하여 반환하는데 사용할 수 있다.
@FunctionalInterface public interface Function<T, R> { R apply(T t); }
기본형 특화
- 오토 박싱으로 인한 비용을 줄이기 위해 기본형을 위한 함수형 인터페이스 또한 제공된다.
- 다음은 기본형 int 변수를 받아 R 타입 객체를 반환한는 함수형 인터페이스이다.
@FunctionalInterface public interface IntFunction<R> { R apply(int value); }
- 이 외에도 DoublePredicate, IntCounsumer와 같이 이름 앞에 형식명이 붙어 특정 형식을 입력으로 받는 다양한 함수형 인터페이스가 존재한다.
- 또한 두 개의 값을 받는 함수형 인터페이스(BiFunction, BiConsmer, ...)도 제공한다.
2.2 형식 추론
- 자바 컴파일러는 람다 표현식이 사용된 context를 파악하여 관련된 함수형 인터페이스를 추론한다.
- 이를 통해 컴파일러는 람다의 시그니처를 추론할 수 있으므로 명시적으로 타입을 포함하지 않을 수 있다.
2.3 지역 변수
- 람다는 파라미터로 넘겨진 변수 외에도 외부의 변수를 자유롭게 사용할 수 있다.
- 단, 해당 변수는 final로 선언되어야 하거나 실질적으로 final로 선언된 변수와 동일하게 사용되어야 한다.
3. 메서드 참조
- 특정 메서드를 호출하는 람다 표현식의 축약형이라고 할 수 있다.
- 메서드명 앞에 구분자(::)를 붙이는 방식으로 메서드 참조를 사용할 수 있다.
IntFunction<String> function1 = (number) -> String.valueOf(number); IntFunction<String> function2 = String::valueOf;
- 메서드 참조 유형*
- 정적 메서드 참조
- String의 valueOf() 메서드는 String::valueOf로 표현 가능하다.
- 인스턴스 메서드 참조
- String의 length() 메서드는 String::length로 표현 가능하다.
IntFunction<String> function1 = (number) -> String.valueOf(number); IntFunction<String> function2 = String::valueOf;
- 기존 객체의 인스턴스 메서드 참조
- 다음과 같이 지역 변수를 참조하는 경우 지역변수::메서드명()으로 표현 가능하다.
String name = "java"; IntSupplier s1 = () -> name.length(); IntSupplier s2 = name::length;
3.1 생성자 참조
클래스명::new 처럼 클래스명과 new 키워드를 이용해서 생성자 참조를 만들 수 있다. 이를 이용하여 인스턴스화 하지 않고 생성자에 접근 하는 것도 가능하다.
'Java' 카테고리의 다른 글
Java의 데이터 입출력 스트림 (0) | 2024.04.24 |
---|---|
[오브젝트] 객체지향 프로그래밍 (0) | 2023.07.26 |
문자열 (0) | 2023.04.20 |