Java 8 이후 큰 변화 중 하나가 함수형 인터페이스(Functional Interface) 기반의 람다 표현식 도입이다.
그중에서도 실무에서 자주 등장하는 인터페이스가 Supplier 와 Runnable이다.
1. 함수형 인터페이스
함수형 인터페이스는 추상 메서드를 단 하나만 갖는 인터페이스이다.
예:
- Runnable
- Supplier<T>
- Consumer<T>
- Function<T, R>
- Predicate<T>
모두 람다식으로 표현할 수 있다.
2. Supplier — “값을 만들어서 공급하는 인터페이스”
@FunctionalInterface
public interface Supplier<T> {
T get();
}
✔ 주요 특징
- 입력 인자가 없다.
- 무언가를 생성하거나 반환하는 역할을 한다.
- 표현식 형태: () -> T
“값을 필요할 때 지연 생성(lazy)하여 제공하는 역할”
2-1. 간단한 예시
Supplier<String> supplier = () -> "Hello Supplier";
System.out.println(supplier.get()); // Hello Supplier
2-2. 객체 생성 캐싱 / Lazy Loading에 활용
Supplier는 지연 초기화(Lazy initialization)에 많이 사용된다.
Supplier<Connection> connectionSupplier = () -> createConnection();
Connection conn = connectionSupplier.get(); // 필요한 시점에 생성
2-3. Optional.orElseGet() 예시
Java Optional의 orElseGet()이 Supplier 기반이다.
String result = optional.orElseGet(() -> loadFromDB());
orElseGet()은 optional이 비어 있을 때만 Supplier를 실행하므로,
불필요한 DB 호출을 막는 효과가 있다.
2-4. 비싼 연산 지연 처리
Supplier<BigDecimal> totalPriceSupplier = () -> calculateTotalPrice();
if (needTotalPrice()) {
BigDecimal price = totalPriceSupplier.get();
}
연산 비용이 높은 계산을 조건에 따라 수행할 때 Supplier는 매우 유용한 도구이다.
3. Runnable — “아무것도 반환하지 않는 작업을 수행하는 인터페이스”
@FunctionalInterface
public interface Runnable {
void run();
}
✔ 주요 특징
- 반환 값이 없다 (void).
- 인자도 없다.
- 오직 “실행할 동작(작업)” 한 가지를 표현하는 인터페이스이다.
- 스레드와 함께 많이 사용된다.
- 표현식 형태: () -> void
3-1. Thread에서 Runnable 사용
가장 전형적인 용도는 스레드 실행이다.
Runnable task = () -> {
System.out.println("작업 실행 중");
};
Thread thread = new Thread(task);
thread.start();
3-2. ExecutorService와 함께 사용
스레드 풀 구조에서도 Runnable은 기본이다.
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> {
System.out.println("비동기 작업 실행");
});
Runnable은 반환값이 필요 없는 작업에 적합하다.
4. Supplier, Runnable 차이점
두 인터페이스 모두 파라미터가 없다는 공통점이 있지만, 목적이 완전히 다르다.
| 인터페이스 | 역할 | 메서드 | 반환 | 사용하는 경우 |
| Supplier<T> | 값을 공급 | T get() | 반환 O | Lazy Loading, 캐싱, 팩토리 메서드, Optional.orElseGet |
| Runnable | 작업을 실행 | void run() | 반환 X | 스레드 실행, 비동기 작업, 이벤트 핸들러 |
Supplier는 “무언가를 반환하는 함수”
Runnable은 “반환 없이 수행하는 작업”
✔ 반환값이 필요하면 → Supplier
예:
- DB에서 값을 읽어오고 싶다
- 객체를 생성하는 함수를 전달하고 싶다
- 값 계산을 지연 실행하고 싶다
✔ 단순 실행 작업이면 → Runnable
예:
- 로그 기록
- 비동기 알림 전송
- UI 버튼 클릭 이벤트
- 배치 작업 실행
6. 예시 코드
6-1. Retry(재시도) 로직 구현
Supplier 버전 (반환값 필요)
public <T> T retry(Supplier<T> action) {
int max = 3;
Exception last = null;
for (int i = 0; i < max; i++) {
try {
return action.get();
} catch (Exception e) {
last = e;
}
}
throw new RuntimeException(last);
}
사용:
String data = retry(() -> loadFromRemoteApi());
Runnable 버전 (반환값 없음)
public void retry(Runnable action) {
int max = 3;
for (int i = 0; i < max; i++) {
try {
action.run();
return;
} catch (Exception ignored) {}
}
}
사용:
retry(() -> sendNotification());
반응형
'IT > JAVA' 카테고리의 다른 글
| CompletableFuture 비동기 처리 (0) | 2026.01.02 |
|---|---|
| Java 멀티스레드와 병렬 실행 (0) | 2025.12.07 |
| getOrDefault() vs computeIfAbsent() (0) | 2025.12.06 |
| ReentrantLock 락 제어 (0) | 2025.12.06 |
| Java 예외: RuntimeException (0) | 2025.12.06 |