함수형 인터페이스
함수형 인터페이스(Functional Interface)는 자바 8에서 소개된 개념으로, 오직 하나의 추상 메소드만 가지는 인터페이스를 말합니다. 이러한 함수형 인터페이스는 람다 표현식(Lambda Expressions)와 메소드 참조(Method References) 등과 함께 사용됩니다.
Java 8에서는 java.util.function 패키지를 통해 많은 표준 함수형 인터페이스를 제공하고 있습니다. 이들 중 몇 가지를 예로 들면:
1. Predicate<T>: T 타입의 객체를 인자로 받아 boolean을 반환하는 함수입니다. `test(T t)`라는 메소드를 가집니다.
2. Consumer<T>: T 타입의 객체를 인자로 받아 결과를 반환하지 않는 함수입니다. `accept(T t)`라는 메소드를 가집니다.
3. Function<T, R>: T 타입의 객체를 인자로 받아 R 타입의 객체를 반환하는 함수입니다. `apply(T t)`라는 메소드를 가집니다.
4. Supplier<T>: 인자를 받지 않고 T 타입의 결과를 반환하는 함수입니다. `get()`이라는 메소드를 가집니다.
함수형 인터페이스는 이 외에도 많은 종류가 있으며, 필요에 따라 사용자가 직접 정의할 수도 있습니다. 함수형 인터페이스를 사용함으로써, 우리는 메소드의 시그니처를 추상화하고, 이를 더 유연하게 활용할 수 있습니다. 이것은 함수형 프로그래밍 패러다임의 중요한 부분입니다.
함수형 인터페이스의 장점
1. 코드의 간결성: 함수형 인터페이스는 람다 표현식과 함께 사용되는 경우가 많습니다. 람다 표현식을 이용하면 익명 클래스를 생성하고 이를 바로 전달하는 것보다 훨씬 간결한 코드를 작성할 수 있습니다.
2. 함수를 일급 객체로 다룰 수 있음: 함수형 인터페이스를 사용하면, 메소드를 변수처럼 다루거나, 다른 메소드의 인자로 전달하거나, 메소드의 결과로서 반환하는 등 함수를 일급 객체로 다룰 수 있습니다.
3. 함수의 추상화: 함수형 인터페이스를 통해 메소드의 시그니처를 추상화할 수 있습니다. 이를 통해 함수의 구현을 유연하게 바꿀 수 있으며, 재사용성도 향상시킬 수 있습니다.
4. 지연 실행(Lazy Evaluation): 함수형 인터페이스는 코드의 지연 실행을 가능하게 합니다. 예를 들어, Supplier<T> 인터페이스를 사용하면, 필요한 시점에만 코드를 실행시킬 수 있습니다.
5. Stream API와의 호환성: 함수형 인터페이스는 Java 8 이후의 Stream API와 잘 호환됩니다. Stream API의 여러 메소드들은 함수형 인터페이스를 인자로 받습니다.
따라서, 함수형 인터페이스를 활용하면 코드의 가독성, 유연성, 재사용성 등을 높일 수 있으며, 람다 표현식, 메소드 참조, Stream API 등과 결합하여 효율적인 코드를 작성할 수 있습니다.
코드의 간결성
먼저, Runnable 인터페이스를 사용하는 익명 클래스를 이용한 예제를 봅시다:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello, world!");
}
};
r.run();
이 코드는 Runnable 인터페이스의 run 메소드를 오버라이드하는 익명 클래스를 생성합니다.
그런데 Runnable 인터페이스는 함수형 인터페이스입니다. 따라서 같은 기능을 람다 표현식을 이용하여 훨씬 간결하게 구현할 수 있습니다:
Runnable r = () -> System.out.println("Hello, world!");
r.run();
람다 표현식을 이용하면, 인터페이스의 메소드를 오버라이드하는 익명 클래스를 생성하는 번거로움을 피할 수 있으며, 코드도 훨씬 간결해집니다. 이처럼 함수형 인터페이스와 람다 표현식을 사용하면 코드를 단순하고 이해하기 쉽게 만들 수 있습니다.
일급 객체(First Class Object)
함수의 추상화
`java.util.function` 패키지의 `Function<T, R>` 인터페이스를 사용하여 메소드의 시그니처를 추상화하는 예를 보여드리겠습니다.
먼저, 정수를 문자열로 변환하는 기능을 가지는 함수를 만들어 보겠습니다.
Function<Integer, String> intToString = Object::toString;
String result = intToString.apply(123);
System.out.println(result); // 출력: "123"
이제, 동일한 `Function<T, R>` 인터페이스를 사용하여 문자열을 모두 대문자로 변환하는 함수를 만들 수 있습니다.
Function<String, String> upperCase = String::toUpperCase;
String result = upperCase.apply("hello");
System.out.println(result); // 출력: "HELLO"
이처럼, `Function<T, R>` 인터페이스를 사용하면 "하나의 입력을 받아서 결과를 생성하는 어떤 동작" 이라는 메소드의 시그니처를 추상화할 수 있습니다. 이렇게 하면, 함수를 일급 객체로 다루고 코드를 더 유연하게 작성할 수 있습니다.
지연 실행
함수형 인터페이스의 지연 실행이란, 실제로 필요한 시점에만 코드를 실행하는 것을 말합니다. 이는 Supplier<T> 함수형 인터페이스를 통해 가능합니다.
예를 들어, 무거운 연산을 수행하는 메소드가 있다고 가정해봅시다. 이 메소드는 호출될 때마다 많은 시간과 리소스를 소비합니다. 하지만 이 메소드를 Supplier<T>로 감싸게 되면, 실제로 get() 메소드를 호출하는 시점에만 연산이 수행됩니다.
Supplier<Double> heavyCalculation = () -> {
// 무거운 연산을 가정합니다.
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Math.random();
};
System.out.println("프로그램 시작");
// 실제 필요한 시점에 get() 메소드를 호출합니다.
System.out.println("결과: " + heavyCalculation.get());
System.out.println("프로그램 종료");
위의 코드에서 heavyCalculation.get()이 호출되기 전까지는 무거운 연산이 수행되지 않습니다. 이렇게 함수형 인터페이스를 사용하면, 필요한 시점에만 코드를 실행하여 프로그램의 성능을 향상시킬 수 있습니다.
'Java' 카테고리의 다른 글
Java Collection Framework-2 (0) | 2024.04.09 |
---|---|
Java Collection Framework-1 (0) | 2024.04.09 |
자바 예외 처리 (0) | 2024.04.09 |
Callable & ExecutorService (0) | 2024.04.09 |
Generics 4 (0) | 2024.04.09 |