1. Functional Interface (함수형 인터페이스)
맵고 짠 건 아무래도… 몸에 안좋으니 Sam삼한 인터페이스로 먹어봅시다.
SAM(Single Abstract Method)을 만족하는 인터페이스는 함수형 인터페이스입니다. 함수형 인터페이스는 Lamdba식으로 표현할 수 있어 코드의 간결성을 높여주고 지연 연산을 통해 성능 향상을 기대할 수 있습니다.
함수형 인터페이스를 작성할 때는 가급적 인터페이스 선언부에 @FunctionalInterface 애노테이션을 함께 써주는 것이 좋습니다. 애노테이션을 사용하여 얻을 수 있는 이점은 1) 인터페이스의 사용 목적을 분명히 하며 2) 애노테이션의 조건을 만족시키지 않을 경우 컴파일 에러를 발생시켜 개발자의 실수를 줄여줍니다.
2. Functional Interface의 종류
2.1 Consumer
Consumer
계열의 인터페이스는 Parameter는 있고 return 값은 없습니다. accept
추상 메소드를 정의하고 있습니다.
인터페이스 이름 | 설명 | 추상 메소드 |
---|---|---|
Consumer |
T 타입 전달인자를 받아 사용합니다. | void accept(T t) |
BiConsumer<T, U> | T와 U 타입 전달인자를 받아 사용합니다. | void accept(T t, U u) |
DoubleConsumer | double 타입을 전달인자로 받아 사용합니다. | void accept(double value) |
IntConsumer | int 타입을 전달인자로 받아 사용합니다. | void accept(int value) |
LongConsumer | long 타입을 전달인자로 받아 사용합니다. | void accept(long value) |
ObjDoubleConsumer | T와 double 타입을 전달인자로 받아 사용합니다. | void accept(T t, double value) |
ObjIntConsumer | T와 int 타입을 전달인자로 받아 사용합니다. | void accept(T t, int value) |
ObjLongConsumer | T와 long 타입을 전달인자로 받아 사용합니다. | void accept(T t, long value) |
// void Stream.forEach(Consumer<? super T> action)
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
numbers.stream().forEach(System.out::println);
2.2 Supplier
Supplier
계열의 인터페이스는 Parameter는 없고 return 값은 있습니다. getXXX
추상 메소드를 정의하고 있습니다.
인터페이스 이름 | 설명 | 추상 메소드 |
---|---|---|
Supplier | T 타입을 반환합니다. | T get() |
BooleanSupplier | boolean 타입을 반환합니다. | boolean getAsBoolean() |
DoubleSupplier | double 타입을 반환합니다. | double getAsDouble() |
IntSupplier | int 타입을 반환합니다. | int getAsInt() |
LongSupplier | long 타입을 반환합니다. | long getAsLong() |
// static <T> Stream<T> Stream.generate(Supplier<? extends T> s)
final String hello = "Hello, Supplier";
Stream.generate(() -> hello)
.limit(3)
.forEach(System.out::println);
2.3 Function
Function
계열의 인터페이스는 Parameter와 return 값 둘 다 있습니다. 타입 변환 목적으로 사용하며, applyXXX
추상 메소드를 정의하고 있습니다.
인터페이스 이름 | 설명 | 추상 메소드 |
---|---|---|
Function<T, R> | T 타입을 전달인자로 받아 R 타입으로 변환합니다. | R apply(T t) |
BiFunction<T, U, R> | T와 U 타입을 전달인자로 받아 R 타입으로 변환합니다. | R apply(T t, U u) |
DoubleFunction | double 타입을 전달인자로 받아 R 타입으로 변환합니다. | R apply(double value) |
IntFunction | int 타입을 전달인자로 받아 R 타입으로 변환합니다. | R apply(int value) |
IntToDoubleFunction / IntToLongFunction | int 타입을 전달인자로 받아 double/long 타입으로 변환합니다. | double applyAsDouble(int value) / long applyAsLong(int value) |
LongToDoubleFunction / LongToIntFunction | long 타입을 전달인자로 받아 double/int 타입으로 변환합니다. | double applyAsDouble(long value) / int applyAsInt(long value) |
ToDoubleBiFunction<T, U> | T와 U 타입을 전달인자로 받아 double 타입으로 변환합니다. | double applyAsDouble(T t, U u) |
ToDoubleFunction | T 타입을 전달인자로 받아 double 타입으로 변환합니다. | double applyAsDouble(T t) |
ToIntBiFunction<T, U> | T와 U 타입을 전달인자로 받아 int 타입으로 변환합니다. | int applyAsInt(T t, U u) |
ToIntFunction | T타입을 전달인자로 받아 int 타입으로 변환합니다. | int applyAsInt(T t) |
ToLongBiFunction<T, U> | T와 U 타입을 전달인자로 받아 long 타입으로 변환합니다. | long applyAsLong(T t, U u) |
ToLongFunction | T 타입을 전달인자로 받아 long 타입으로 변환합니다. | long applyAsLong(T t) |
// <R> Stream<R> Stream.map(Function<? super T, ? extends R> mapper)
List<String> numbers = List.of("2", "3", "1");
numbers.stream().map(n -> Integer.valueOf(n))
.sorted()
.forEach(System.out::println);
2.4 Operator
Operator
계열의 인터페이스는 Function
계열과 동일하게 Parameter와 return 값이 있습니다. 값을 연산하기 위해서 사용하며, applyXXX
추상 메소드를 정의하고 있습니다.
인터페이스 이름 | 설명 | 추상 메소드 |
---|---|---|
BinaryOperator | T 타입 두 개를 전달인자로 받아 연산하여 T 타입을 반환합니다. | T apply(T left, T right) |
UnaryOperator | T 타입을 전달인자로 받아 연산하여 T 타입을 반환합니다. | T apply(T t) |
DoubleBinaryOperator | double 타입 두 개를 전달인자로 받아 연산하여 double 타입을 반환합니다. | double applyAsDouble(double left, double right) |
DoubleUnaryOperator | double 타입을 전달인자로 받아 연산하여 double 타입을 반환합니다. | double applyAsDouble(double operand) |
IntBinaryOperator | int 타입 두 개를 전달인자로 받아 연산하여 int 타입을 반환합니다. | int applyAsInt(int left, int right) |
IntUnaryOperator | int 타입을 전달인자로 받아 int 타입을 반환합니다. | int applyAsInt(int operand) |
LongBinaryOperator | long 타입 두 개를 전달인자로 받아 연산하여 long 타입을 반환합니다. | long applyAsLong(long left, long right) |
LongUnaryOperator | long 타입을 전달인자로 받아 long 타입을 반환합니다. | long applyAsLong(long operand) |
// Optional<T> Stream.reduce(BinaryOperator<T> accumulator)
List<Integer> numbers = List.of(1, 2, 3, 4, 5);
int sum = numbers.stream()
.reduce((left, right) -> left + right)
.get();
2.5 Predicate
Predicate 계열의 인터페이스는 Parameter와 boolean 타입 return 값이 있습니다. 전달인자를 검사하기 위해서 사용하며, test 추상 메소드를 정의하고 있습니다.
인터페이스 이름 | 설명 | 추상 메소드 |
---|---|---|
Predicate | T 타입 전달인자를 받아 boolean 타입을 반환합니다. | boolean test(T t) |
BiPredicate<T, U> | T와 U 타입 전달인자를 받아 boolean 타입을 반환합니다. | boolean test(T t, U u) |
DoublePredicate | double 타입을 전달인자로 받아 boolean 타입을 반환합니다. | boolean test(double value) |
IntPredicate | int 타입을 전달인자로 받아 boolean 타입을 반환합니다. | boolean test(int value) |
LongPredicate | long 타입을 전달인자로 받아 boolean 타입을 반환합니다. | boolean test(long value) |
// boolean Stream.allMatch(Predicate<? super T> predicate)
List<Integer> scores = List.of(87, 99, 72, 80, 95);
boolean result = scores.stream().allMatch(s -> 90 < s);
3. 사용자 정의 Functional Interface
Java 표준 스팩에 포함된 유용한 함수형 인터페이스만 활용해도 충분합니다!
함수형 인터페이스는 단일 추상 메소드로 구성되어야 합니다. 단일 추상 메소드 외에도 기본(default), 정적(static), Object 클래스의 메소드를 추가로 포함할 수 있습니다.
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod1(); // 추상 메소드
default void myMethod2() {}; // 기본 메소드
static void myMethod3() {}; // 정적 메소드
}
public class Foo {
MyFunctionalInterface myFuncIf = () -> System.out.println("Hello, world!");
}
References
https://www.baeldung.com