Skip to content

Latest commit

Β 

History

History
357 lines (255 loc) Β· 19.4 KB

File metadata and controls

357 lines (255 loc) Β· 19.4 KB

item 44 : ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λΌ

μžλ°”μ—μ„œ λžŒλ‹€ 지원이 μΆ”κ°€λ˜λ©΄μ„œ API μž‘μ„± 방식에도 큰 λ³€ν™”κ°€ 생겼닀. 특히, 과거의 ν…œν”Œλ¦Ώ λ©”μ„œλ“œ νŒ¨ν„΄ λŒ€μ‹  ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•˜λŠ” 방식이 μ£Όλͺ©λ°›κ³  μžˆλ‹€.

이둜 인해 ν•¨μˆ˜ 객체λ₯Ό λ§€κ°œλ³€μˆ˜λ‘œ λ°›λŠ” μƒμ„±μžμ™€ λ©”μ„œλ“œλ₯Ό 더 많이 μž‘μ„±ν•˜κ²Œ λ˜μ—ˆμœΌλ©°, μ˜¬λ°”λ₯Έ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ νƒ€μž…μ„ μ„ νƒν•˜λŠ” 것이 μ€‘μš”ν•΄μ‘Œλ‹€. 즉, ν•¨μˆ˜ν˜• λ§€κ°œλ³€μˆ˜ νƒ€μž…μ„ μ˜¬λ°”λ₯΄κ²Œ 선택해야 ν•œλ‹€.

μžλ°”μ˜ ν‘œμ€€ λΌμ΄λΈŒλŸ¬λ¦¬μ—μ„œλŠ” 이미 λ‹€μ–‘ν•œ μš©λ„μ˜ ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ 제곡되고 있으며, 이듀을 ν™œμš©ν•˜λ©΄ λΆˆν•„μš”ν•˜κ²Œ μƒˆλ‘œμš΄ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μž‘μ„±ν•˜μ§€ μ•Šκ³ λ„ ν•„μš”ν•œ κΈ°λŠ₯을 κ΅¬ν˜„ν•  수 μžˆλ‹€. 특히 java.util.function νŒ¨ν‚€μ§€μ—λŠ” ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ„ μ§€μ›ν•˜κΈ° μœ„ν•΄ 총 43개의 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ μ€€λΉ„λ˜μ–΄ μžˆλ‹€.

1. ν…œν”Œλ¦Ώ λ©”μ„œλ“œ νŒ¨ν„΄μ—μ„œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ μ „ν™˜

κ³Όκ±°μ—λŠ” μƒμœ„ 클래슀의 κΈ°λ³Έ λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ν•˜μ—¬ λ™μž‘μ„ 맞좀 μ„€μ •ν•˜λŠ” ν…œν”Œλ¦Ώ λ©”μ„œλ“œ νŒ¨ν„΄μ„ 많이 μ‚¬μš©ν–ˆλ‹€. ν•˜μ§€λ§Œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ™€ λžŒλ‹€λ₯Ό μ΄μš©ν•˜λ©΄ 이보닀 κ°„κ²°ν•˜κ³  μœ μ§€λ³΄μˆ˜ν•˜κΈ° 쒋은 APIλ₯Ό λ§Œλ“€ 수 μžˆλ‹€.

μ•„λž˜ μ˜ˆμ œλŠ” ν…œν”Œλ¦Ώ λ©”μ„œλ“œ νŒ¨ν„΄μ„ μ‚¬μš©ν•œ 전톡적인 방식과 ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•œ ν˜„λŒ€μ μΈ λ°©μ‹μ˜ 비ꡐ이닀.

// ν…œν”Œλ¦Ώ λ©”μ„œλ“œ νŒ¨ν„΄ 방식
class CustomCache<K, V> extends LinkedHashMap<K, V> {
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return size() > 100;
    }
}

μœ„ μ½”λ“œλŠ” LinkedHashMap을 ν™•μž₯ν•΄ removeEldestEntry λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ν•˜μ—¬ μΊμ‹œ κΈ°λŠ₯을 μΆ”κ°€ν•œ 것이닀. 맡에 μƒˆλ‘œμš΄ ν‚€λ₯Ό μΆ”κ°€ν•˜λŠ” put λ©”μ„œλ“œλŠ” 이 λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ trueκ°€ λ°˜ν™˜λ˜λ©΄ λ§΅μ—μ„œ κ°€μž₯ 였래된 μ›μ†Œλ₯Ό 제 κ±°ν•œλ‹€. μ˜ˆμ»¨λŒ€ removeEldestEntryλ₯Ό λ‹€μŒμ²˜λŸΌ μž¬μ •μ˜ν•˜λ©΄ 맡에 μ›μ†Œκ°€ 100개 κ°€ 될 λ•ŒκΉŒμ§€ 컀지닀가, κ·Έ 이상이 되면 μƒˆλ‘œμš΄ ν‚€κ°€ λ”ν•΄μ§ˆ λ•Œλ§ˆλ‹€ κ°€μž₯ 였래 된 μ›μ†Œλ₯Ό ν•˜λ‚˜μ”© μ œκ±°ν•œλ‹€.

즉, κ°€μž₯ 졜근 μ›μ†Œ 100개λ₯Ό μœ μ§€ν•œλ‹€.

잘 λ™μž‘ν•˜μ§€λ§Œ λžŒλ‹€λ₯Ό μ‚¬μš©ν•˜λ©΄ 훨씬 잘 ν•΄λ‚Ό 수 μžˆλ‹€. removeEldestEntry 선언을 보면 이 ν•¨μˆ˜ κ°μ²΄λŠ” Map.Entry<K, V>λ₯Ό λ°›μ•„ boolean 을 λ°˜ν™˜ν•΄μ•Ό ν•  것 κ°™μ§€λ§Œ, κΌ­ κ·Έλ ‡μ§€λŠ” μ•Šλ‹€.γ€ŒemoveEldestEntryλŠ” size()λ₯Ό 호 μΆœν•΄ λ§΅ μ•ˆμ˜ μ›μ†Œ 수λ₯Ό μ•Œμ•„λ‚΄λŠ”λ°, removeEldestEntryκ°€ μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œλΌμ„œ κ°€λŠ₯ν•œ 방식이닀. ν•˜μ§€λ§Œ μƒμ„±μžμ— λ„˜κΈ°λŠ” ν•¨μˆ˜ κ°μ²΄λŠ” 이 맡의 μΈμŠ€ν„΄μŠ€ λ©”μ„œλ“œκ°€ μ•„λ‹ˆλ‹€.

νŒ©ν„°λ¦¬λ‚˜ μƒμ„±μžλ₯Ό ν˜ΈμΆœν•  λ•ŒλŠ” 맡의 μΈμŠ€ν„΄μŠ€κ°€ μ‘΄μž¬ν•˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€. λ”°λΌμ„œ 맡은 자기 μžμ‹ λ„ ν•¨μˆ˜ 객체에 κ±΄λ„€μ€˜μ•Ό ν•œλ‹€.

이λ₯Ό λ°˜μ˜ν•˜μ—¬ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ‘œ μ „ν™˜ν•˜λ©΄ λ‹€μŒκ³Ό 같이 κ΅¬ν˜„ν•  수 μžˆλ‹€.

// ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ BiPredicateλ₯Ό μ‚¬μš©ν•˜μ—¬ μΊμ‹œμ˜ λ™μž‘μ„ μœ μ—°ν•˜κ²Œ μ„€μ •ν•˜λŠ” 방식
import java.util.function.BiPredicate;
import java.util.LinkedHashMap;
import java.util.Map;

class CustomCache<K, V> extends LinkedHashMap<K, V> {
    // 제거 기쀀을 μ •μ˜ν•˜λŠ” BiPredicate μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ 쑰건을 μ €μž₯
    private final BiPredicate<Map<K, V>, Map.Entry<K, V>> removalCriteria;

    // μƒμ„±μžμ—μ„œ 제거 기쀀을 λ°›μŒ
    public CustomCache(BiPredicate<Map<K, V>, Map.Entry<K, V>> removalCriteria) {
        this.removalCriteria = removalCriteria;
    }

    // LinkedHashMap의 removeEldestEntry λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ν•˜μ—¬
    // μ„€μ •ν•œ 쑰건에 따라 였래된 ν•­λͺ©μ„ μ œκ±°ν• μ§€ κ²°μ •
    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        // BiPredicateλ₯Ό μ‚¬μš©ν•˜μ—¬ 쑰건에 따라 true/false λ°˜ν™˜
        return removalCriteria.test(this, eldest);
    }
}

// μ‚¬μš© 예: μΊμ‹œ 객체 생성
// μΊμ‹œ 객체λ₯Ό μƒμ„±ν•˜λ©΄μ„œ map의 크기가 100을 μ΄ˆκ³Όν•˜λ©΄ κ°€μž₯ 였래된 ν•­λͺ©μ„ μ œκ±°ν•˜λ„λ‘ μ„€μ •
CustomCache<String, String> cache = new CustomCache<>((map, entry) -> map.size() > 100);
  1. BiPredicate μ‚¬μš©: BiPredicate<Map<K, V>, Map.Entry<K, V>>λ₯Ό μ‚¬μš©ν•˜μ—¬ λ§΅κ³Ό μ—”νŠΈλ¦¬λ₯Ό 인수둜 λ°›μ•„ boolean 값을 λ°˜ν™˜ν•˜λŠ” 쑰건을 μ„€μ •ν•œλ‹€. 이 쑰건을 removalCriteriaλΌλŠ” λ³€μˆ˜μ— μ €μž₯ν•œλ‹€.
  2. μƒμ„±μž: CustomCache 클래슀의 μƒμ„±μžμ—μ„œ removalCriteriaλΌλŠ” BiPredicateλ₯Ό 인수둜 λ°›μ•„, μΊμ‹œμ˜ ν•­λͺ© 제거 기쀀을 μ„€μ •ν•œλ‹€. μ΄λŠ” λ™μ μœΌλ‘œ 쑰건을 μ§€μ •ν•  수 있게 ν•œλ‹€.
  3. removeEldestEntry λ©”μ„œλ“œ: LinkedHashMap의 removeEldestEntry λ©”μ„œλ“œλ₯Ό μž¬μ •μ˜ν•˜μ—¬, removalCriteria에 따라 였래된 ν•­λͺ©μ„ μ œκ±°ν• μ§€ κ²°μ •ν•œλ‹€. μ΄λ•Œ this(λ§΅ 자체)와 eldest(κ°€μž₯ 였래된 ν•­λͺ©)λ₯Ό 인수둜 test λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ 기쀀을 ν™•μΈν•œλ‹€.
  4. μ‚¬μš© 예: CustomCache 객체λ₯Ό 생성할 λ•Œ λžŒλ‹€μ‹ (map, entry) -> map.size() > 100을 μ „λ‹¬ν•˜μ—¬, 맡의 크기가 100을 μ΄ˆκ³Όν•  경우 κ°€μž₯ 였래된 ν•­λͺ©μ„ μ œκ±°ν•˜λ„λ‘ μ„€μ •ν•œλ‹€.

이 μ½”λ“œ κ΅¬μ‘°λŠ” μΊμ‹œμ˜ ν•­λͺ© 제거 기쀀을 λ™μ μœΌλ‘œ μ„€μ •ν•  수 μžˆμ–΄ μœ μ—°μ„±μ„ 높이며, κΈ°μ‘΄ ν…œν”Œλ¦Ώ λ©”μ„œλ“œ νŒ¨ν„΄λ³΄λ‹€ κ°„κ²°ν•˜κ³  λͺ¨λ“ˆν™”λœ λ°©μ‹μœΌλ‘œ κ΅¬ν˜„ν•  수 있게 ν•΄μ€€λ‹€.

λžŒλ‹€λ₯Ό ν™œμš©ν•œ 이 방식은 더 μœ μ—°ν•˜λ©°, removeEldestEntryλ₯Ό μž¬μ •μ˜ν•  ν•„μš” 없이 λ‹€λ₯Έ 쑰건으둜 μ‰½κ²Œ 맞좀 μ„€μ •ν•  수 μžˆλ‹€.

2. ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ ν™œμš©

μžλ°”μ˜ java.util.function νŒ¨ν‚€μ§€μ—λŠ” λ‹€μ–‘ν•œ ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ 제곡되며, 이λ₯Ό ν™œμš©ν•˜λ©΄ API 섀계가 λ‹¨μˆœν•˜κ³  μΌκ΄€λ˜κ²Œ μœ μ§€λœλ‹€.

ν•„μš”ν•œ μš©λ„μ— λ§žλŠ” 게 μžˆλ‹€λ©΄, 직접 κ΅¬ν˜„ν•˜μ§€ 말고 ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό ν™œμš©ν•˜λΌ. 그러면 APIκ°€ λ‹€λ£¨λŠ” κ°œλ…μ˜ μˆ˜κ°€ 쀄어듀어 읡히기 더 μ‰¬μ›Œ μ§„λ‹€.

μ˜ˆμ»¨λŒ€ Predicate μΈν„°νŽ˜μ΄μŠ€λŠ” ν”„λ ˆλ””ν‚€νŠΈ(predicate)듀을 μ‘°ν•©ν•˜λŠ” λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€. μ•žμ˜ LinkedHashMap μ˜ˆμ—μ„œλŠ” 직접 λ§Œλ“  EldestEntryRemovalFunction λŒ€μ‹  ν‘œμ€€ μΈν„°νŽ˜μ΄μŠ€μΈ BiPredicate<Map<K,V>, Map.Entry<K,V>λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.

1) μ£Όμš” ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” 크게 λ‹€μŒκ³Ό 같은 μœ ν˜•μ΄ μžˆλ‹€.

기본적으둜 μžλ°”μ˜ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” λ‹€μŒκ³Ό 같은 μœ ν˜•μœΌλ‘œ λ‚˜λ‰©λ‹ˆλ‹€. 이 μΈν„°νŽ˜μ΄μŠ€λ“€μ€ 객체 μ°Έμ‘° νƒ€μž…μ„ μœ„ν•œ κΈ°λ³Έ μΈν„°νŽ˜μ΄μŠ€λ“€λ‘œ, μ‹€λ¬΄μ—μ„œ 자주 ν™œμš©λ©λ‹ˆλ‹€.

μΈν„°νŽ˜μ΄μŠ€ ν•¨μˆ˜ μ‹œκ·Έλ‹ˆμ²˜ μ„€λͺ… μ‚¬μš© μ˜ˆμ‹œ
UnaryOperator<T> T apply(T t) 인수λ₯Ό ν•˜λ‚˜ λ°›μ•„ 같은 νƒ€μž…μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€. String::toLowerCase
BinaryOperator<T> T apply(T t1, T t2) 인수λ₯Ό 두 개 λ°›μ•„ 같은 νƒ€μž…μ„ λ°˜ν™˜ν•©λ‹ˆλ‹€. BigInteger::add
Predicate<T> boolean test(T t) 인수λ₯Ό ν•˜λ‚˜ λ°›μ•„ boolean을 λ°˜ν™˜ν•©λ‹ˆλ‹€. Collection::isEmpty
Function<T, R> R apply(T t) μΈμˆ˜μ™€ λ°˜ν™˜ νƒ€μž…μ΄ λ‹€λ₯Ό λ•Œ μ‚¬μš©ν•©λ‹ˆλ‹€. Arrays::asList
Supplier<T> T get() 인수λ₯Ό λ°›μ§€ μ•Šκ³  값을 λ°˜ν™˜ν•©λ‹ˆλ‹€. Instant::now
Consumer<T> void accept(T t) 인수λ₯Ό ν•˜λ‚˜ λ°›κ³  λ°˜ν™˜κ°’μ΄ μ—†μŠ΅λ‹ˆλ‹€. System.out::println

μ˜ˆμ‹œ - Predicate와 BiPredicate ν™œμš©

Predicate와 BiPredicateλŠ” 쑰건을 ν‘œν˜„ν•˜λŠ” 데 μ‚¬μš©λ˜λ©°, 각각 ν•˜λ‚˜ λ˜λŠ” 두 개의 인수λ₯Ό λ°›μ•„ boolean을 λ°˜ν™˜ν•œλ‹€. 예λ₯Ό λ“€μ–΄, νŠΉμ • 크기 이상일 λ•Œ μΊμ‹œ ν•­λͺ©μ„ μ‚­μ œν•˜λ„λ‘ 쑰건을 μ„€μ •ν•  수 μžˆλ‹€.

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.BiPredicate;

class CustomCache<K, V> extends LinkedHashMap<K, V> {
    private final BiPredicate<Map<K, V>, Map.Entry<K, V>> removalCriteria;

    public CustomCache(BiPredicate<Map<K, V>, Map.Entry<K, V>> removalCriteria) {
        this.removalCriteria = removalCriteria;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
        return removalCriteria.test(this, eldest);
    }
}

// μ‚¬μš© 예: μΊμ‹œ 크기가 100을 μ΄ˆκ³Όν•˜λ©΄ 였래된 ν•­λͺ© 제거
CustomCache<String, String> cache = new CustomCache<>((map, entry) -> map.size() > 100);

μœ„ μ½”λ“œλŠ” BiPredicateλ₯Ό μ‚¬μš©ν•΄ νŠΉμ • 크기λ₯Ό λ„˜μ„ λ•Œ μΊμ‹œ ν•­λͺ©μ„ μ œκ±°ν•˜λ„λ‘ μ„€μ •ν•œ μ˜ˆμ‹œμ΄λ‹€.

예제

  1. Predicate - 쑰건 검사

    Predicate<String> isEmpty = String::isEmpty;
    System.out.println(isEmpty.test(""));   // true
    System.out.println(isEmpty.test("abc")); // false
  2. Function - νƒ€μž… λ³€ν™˜

    Function<String, Integer> toLength = String::length;
    System.out.println(toLength.apply("Hello")); // 5
  3. Supplier - κ°’ 생성

    Supplier<Double> randomSupplier = Math::random;
    System.out.println(randomSupplier.get()); // 0.12345 (μž„μ˜μ˜ κ°’)
  4. Consumer - κ°’ μ†ŒλΉ„

    Consumer<String> print = System.out::println;
    print.accept("Hello, world!"); // Hello, world!
  5. UnaryOperator - κ°’ λ³€ν™˜

    UnaryOperator<String> toLowerCase = String::toLowerCase;
    System.out.println(toLowerCase.apply("HELLO")); // hello

2) ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ ν™œμš©μ˜ 이점

κΈ°μ‘΄μ—λŠ” μ»€μŠ€ν…€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μž‘μ„±ν•˜μ—¬ κΈ°λŠ₯을 κ΅¬ν˜„ν•˜λŠ” κ²½μš°κ°€ λ§Žμ•˜μ§€λ§Œ, ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ λ‹€μŒκ³Ό 같은 이점을 λˆ„λ¦΄ 수 μžˆλ‹€:

  • μ½”λ“œμ˜ κ°„κ²°μ„±: μ€‘λ³΅λ˜λŠ” μΈν„°νŽ˜μ΄μŠ€ 생성을 ν”Όν•  수 μžˆλ‹€.
  • API의 일관성: μžλ°” κ°œλ°œμžλ“€μ΄ 이미 μ΅μˆ™ν•œ ν‘œμ€€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ―€λ‘œ, μƒˆλ‘œμš΄ κ°œλ…μ„ 읡힐 ν•„μš”κ°€ 쀄어든닀.
  • λ””ν΄νŠΈ λ©”μ„œλ“œμ˜ ν™œμš©: ν‘œμ€€ μΈν„°νŽ˜μ΄μŠ€λŠ” ν•„μš”ν•œ 경우 λ©”μ„œλ“œλ₯Ό μ‘°ν•©ν•˜κ±°λ‚˜ λ³€ν˜•ν•  수 μžˆλŠ” μœ μš©ν•œ λ””ν΄νŠΈ λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜μ—¬ λ‹€λ₯Έ μ½”λ“œμ™€ μ‰½κ²Œ μƒν˜Έ 운용이 κ°€λŠ₯ν•˜λ‹€.

예λ₯Ό λ“€μ–΄, Predicate μΈν„°νŽ˜μ΄μŠ€λŠ” 쑰건을 μ‘°ν•©ν•  수 μžˆλŠ” and, or, negate λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•˜μ—¬ λ³΅μž‘ν•œ 쑰건을 κ°„νŽΈν•˜κ²Œ ν‘œν˜„ν•  수 μžˆλ‹€.

3. κΈ°λ³Έ νƒ€μž…μš© ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

κΈ°λ³Έ νƒ€μž…μ„ μœ„ν•΄ java.util.function νŒ¨ν‚€μ§€μ—μ„œλŠ” κΈ°λ³Έ νƒ€μž…μš© ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ„ μ œκ³΅ν•œλ‹€. 박싱을 ν”Όν•˜κ³  μ„±λŠ₯을 높이기 μœ„ν•΄ κΈ°λ³Έ νƒ€μž…μ„ μœ„ν•œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ λ³€ν˜•λ„ μ œκ³΅ν•œλ‹€. 예λ₯Ό λ“€μ–΄, IntPredicate, LongFunction, DoubleConsumer 등이닀. κΈ°λ³Έ νƒ€μž…μ„ μ§€μ›ν•˜λŠ” μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ λ°•μ‹± μ˜€λ²„ν—€λ“œλ₯Ό ν”Όν•  수 μžˆμ–΄ μ„±λŠ₯이 κ°œμ„ λœλ‹€.

예제

IntPredicate isEven = x -> x % 2 == 0;
System.out.println(isEven.test(4)); // true
System.out.println(isEven.test(5)); // false

μžλ°” κΈ°λ³Έ νƒ€μž…μ„ μœ„ν•œ λ³€ν˜•μ€ λ‹€μŒκ³Ό 같은 κ·œμΉ™μ— 따라 λ„€μ΄λ°λœλ‹€.

  1. νƒ€μž… 접두어: Int, Long, Double을 μ‚¬μš©ν•˜μ—¬ κΈ°λ³Έ νƒ€μž…μ„ λͺ…μ‹œ
  2. κΈ°λŠ₯: Predicate, Function, Consumer λ“± μ£Όμš” κΈ°λŠ₯을 반영
  3. λ³€ν˜• μ˜ˆμ‹œ:
    • IntPredicate: boolean test(int value) ν˜•νƒœλ‘œ int νƒ€μž…μ„ λ°›λŠ”λ‹€.
    • LongToIntFunction: int applyAsInt(long value) ν˜•νƒœλ‘œ long νƒ€μž…μ˜ 인수λ₯Ό λ°›κ³  intλ₯Ό λ°˜ν™˜ν•œλ‹€.

μ˜ˆμ‹œ - κΈ°λ³Έ νƒ€μž…μš© ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

κΈ°λ³Έ νƒ€μž…μ„ μ‚¬μš©ν•˜μ—¬ νŠΉμ • 쑰건을 κ²€μ‚¬ν•˜λŠ” 예제

import java.util.function.IntPredicate;

public class BasicTypePredicateExample {
    public static void main(String[] args) {
        IntPredicate isEven = value -> value % 2 == 0;
        
        System.out.println(isEven.test(4)); // true
        System.out.println(isEven.test(5)); // false
    }
}

μœ„ μ˜ˆμ œμ—μ„œλŠ” IntPredicateλ₯Ό μ‚¬μš©ν•˜μ—¬ μˆ«μžκ°€ μ§μˆ˜μΈμ§€ μ—¬λΆ€λ₯Ό κ²€μ‚¬ν•œλ‹€.

4. 직접 μž‘μ„±ν•΄μ•Ό ν•˜λŠ” 경우

ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ 거의 λŒ€λΆ€λΆ„μ˜ 경우λ₯Ό μ»€λ²„ν•˜μ§€λ§Œ, λ‹€μŒκ³Ό 같은 κ²½μš°μ—λŠ” 직접 μž‘μ„±ν•˜λŠ” 것이 더 λ‚˜μ„ 수 μžˆλ‹€.

  • νŠΉμ • κ·œμ•½μ„ 따라야 ν•  경우: 예λ₯Ό λ“€μ–΄, ComparatorλŠ” ToIntBiFunctionκ³Ό ꡬ쑰가 λΉ„μŠ·ν•˜μ§€λ§Œ, 비ꡐ κΈ°λŠ₯을 λͺ…ν™•νžˆ λ‚˜νƒ€λ‚΄κ³  있으며 νŠΉμ • κ·œμ•½(μˆœμ„œ 비ꡐ)을 λ”°λ₯΄λ„둝 μ„€κ³„λ˜μ–΄ μžˆλ‹€.
  • 자주 μ‚¬μš©λ˜λ©° μ΄λ¦„λ§ŒμœΌλ‘œ μš©λ„κ°€ λͺ…ν™•ν•œ 경우: 자주 μ‚¬μš©λ˜λ©° λͺ…ν™•ν•œ 이름을 κ°€μ§€λ©΄ μ½”λ“œ 가독성이 λ†’μ•„μ§„λ‹€.
  • 좔가적인 λ””ν΄νŠΈ λ©”μ„œλ“œκ°€ ν•„μš”ν•œ 경우: μΈν„°νŽ˜μ΄μŠ€ λ‚΄μ—μ„œ λ©”μ„œλ“œλ₯Ό μ‘°ν•©ν•˜κ±°λ‚˜ λ³€ν˜•ν•˜λŠ” λ””ν΄νŠΈ λ©”μ„œλ“œκ°€ ν•„μš”ν•œ κ²½μš°κ°€ μžˆλ‹€.

μ˜ˆμ‹œ - 3개의 λ§€κ°œλ³€μˆ˜λ₯Ό λ°›λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€

java.util.function νŒ¨ν‚€μ§€μ—λŠ” λ§€κ°œλ³€μˆ˜ 3개λ₯Ό λ°›λŠ” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€κ°€ μ—†μœΌλ―€λ‘œ, 직접 μž‘μ„±ν•  수 μžˆλ‹€.

@FunctionalInterface
interface TriFunction<T, U, V, R> {
    R apply(T t, U u, V v);
}

// μ‚¬μš© μ˜ˆμ‹œ
TriFunction<Integer, Integer, Integer, Integer> sumThree = (a, b, c) -> a + b + c;
System.out.println(sumThree.apply(1, 2, 3)); // 6

5. μ£Όμ˜μ‚¬ν•­: ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 닀쀑 μ •μ˜ν•˜μ§€ μ•ŠκΈ°

μ„œλ‘œ λ‹€λ₯Έ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 같은 μœ„μΉ˜μ˜ 인수둜 λ°›λŠ” λ©”μ„œλ“œλ₯Ό 닀쀑 μ •μ˜ν•˜λŠ” 것은 ν”Όν•΄μ•Ό ν•œλ‹€. 예λ₯Ό λ“€μ–΄, ExecutorService의 submit λ©”μ„œλ“œλŠ” Callableκ³Ό Runnable을 닀쀑 μ •μ˜ν•˜λŠ”λ°, 이λ₯Ό μ‚¬μš©ν•  λ•Œ ν˜•λ³€ν™˜μ΄ ν•„μš”ν•œ κ²½μš°κ°€ 생긴닀. μ΄λŠ” λΆˆν•„μš”ν•œ ν˜Όλž€μ„ μ΄ˆλž˜ν•  수 μžˆλ‹€.

λ”°λΌμ„œ, 같은 μœ„μΉ˜μ˜ 인수둜 μ„œλ‘œ λ‹€λ₯Έ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ°›μ§€ μ•Šλ„λ‘ μ£Όμ˜ν•˜λŠ” 것이 μ’‹λ‹€.

μ΄λŠ” API μ„€κ³„μ—μ„œ λͺ¨ν˜Έν•¨μ„ 쀄이고, μ‚¬μš©μžκ°€ μ½”λ“œμ˜ μ˜λ„λ₯Ό λͺ…ν™•ν•˜κ²Œ 이해할 수 μžˆλ„λ‘ 도와쀀닀.

{% hint style="info" %} μ•ˆ 쒋은 μ˜ˆμ‹œ {% endhint %}

μ•„λž˜λŠ” μ„œλ‘œ λ‹€λ₯Έ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€(Callableκ³Ό Runnable)λ₯Ό 같은 μœ„μΉ˜μ—μ„œ 받도둝 닀쀑 μ •μ˜λœ λ©”μ„œλ“œ

import java.util.concurrent.Callable;

public class ExecutorServiceExample {
    // Runnable을 인수둜 λ°›λŠ” λ©”μ„œλ“œ
    public void submit(Runnable task) {
        System.out.println("Runnable version called");
        task.run();
    }

    // Callable을 인수둜 λ°›λŠ” λ©”μ„œλ“œ
    public <T> T submit(Callable<T> task) throws Exception {
        System.out.println("Callable version called");
        return task.call();
    }

    public static void main(String[] args) throws Exception {
        ExecutorServiceExample service = new ExecutorServiceExample();

        // Runnable을 μ‚¬μš©ν•œ 경우
        service.submit(() -> System.out.println("Running task..."));

        // Callable을 μ‚¬μš©ν•œ 경우
        String result = service.submit(() -> {
            System.out.println("Calling task...");
            return "Task result";
        });
        System.out.println("Result: " + result);

        // ν˜Όλž€μ„ μΌμœΌν‚¬ 수 μžˆλŠ” λͺ¨ν˜Έν•œ 상황
        service.submit((Callable<Void>) () -> {
            System.out.println("Ambiguous task...");
            return null;
        });
    }
}
  • submit(Runnable task): Runnable μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ°›λŠ” submit λ©”μ„œλ“œμ΄λ‹€.
  • submit(Callable<T> task): Callable μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ°›λŠ” μ œλ„€λ¦­ submit λ©”μ„œλ“œμ΄λ‹€.
  • 두 λ©”μ„œλ“œλŠ” λͺ¨λ‘ 같은 μ΄λ¦„μ˜ submit λ©”μ„œλ“œμ§€λ§Œ, 인수의 νƒ€μž…λ§Œ λ‹€λ₯΄κ²Œ μ •μ˜λ˜μ–΄ μžˆλ‹€.

문제점

이 닀쀑 μ •μ˜λœ λ©”μ„œλ“œλŠ” Runnableκ³Ό Callable 쀑 μ–΄λ–€ 것을 ν˜ΈμΆœν• μ§€ λͺ¨ν˜Έν•œ κ²½μš°κ°€ λ°œμƒν•  수 μžˆλ‹€. 특히 λžŒλ‹€ ν‘œν˜„μ‹μœΌλ‘œ 전달할 λ•Œ 문제λ₯Ό μΌμœΌν‚¬ 수 μžˆμŠ΅λ‹ˆλ‹€. Runnableκ³Ό Callable은 λ‘˜ λ‹€ μΈμˆ˜κ°€ μ—†μœΌλ―€λ‘œ λžŒλ‹€μ‹μœΌλ‘œλŠ” λͺ…ν™•νžˆ κ΅¬λΆ„λ˜μ§€ μ•ŠλŠ”λ‹€. 예λ₯Ό λ“€μ–΄, λ‹€μŒκ³Ό 같이 ν˜ΈμΆœν•  λ•Œ λ¬Έμ œκ°€ λœλ‹€.

service.submit(() -> {
    System.out.println("Ambiguous task...");
    return null;
});

μœ„ μ½”λ“œλŠ” Runnable인지 Callable인지 μ»΄νŒŒμΌλŸ¬κ°€ κ΅¬λΆ„ν•˜μ§€ λͺ»ν•˜μ—¬ 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¨λ‹€. 이λ₯Ό ν•΄κ²°ν•˜λ €λ©΄ λͺ…μ‹œμ μΈ ν˜•λ³€ν™˜μ΄ ν•„μš”ν•˜λ‹€.

service.submit((Callable<Void>) () -> {
    System.out.println("Callable task...");
    return null;
});

λͺ…μ‹œμ  ν˜•λ³€ν™˜μ„ 톡해 Callableμ΄λΌλŠ” μ˜λ„λ₯Ό λͺ…ν™•νžˆ ν–ˆμ§€λ§Œ, μ΄λŠ” μ½”λ“œλ₯Ό 더 λ³΅μž‘ν•˜κ²Œ λ§Œλ“€κ³  μ‹€μˆ˜ν•  κ°€λŠ₯성을 높인닀.

ν•΄κ²° 방법: ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€ 닀쀑 μ •μ˜λ₯Ό ν”Όν•˜κΈ°

이 문제λ₯Ό ν”Όν•˜λ €λ©΄ μ„œλ‘œ λ‹€λ₯Έ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 같은 μœ„μΉ˜μ˜ 인수둜 λ°›λŠ” 닀쀑 μ •μ˜λ₯Ό ν”Όν•˜λŠ” 것이 μ’‹λ‹€. 예λ₯Ό λ“€μ–΄, submit λ©”μ„œλ“œλ₯Ό ν•˜λ‚˜λ‘œ ν†΅μΌν•˜κ³ , Runnableμ΄λ‚˜ Callable이 ν•„μš”ν•  경우 래퍼 λ©”μ„œλ“œλ‘œ κ΅¬λΆ„ν•˜λŠ” 방식이 κ°€λŠ₯ν•˜λ‹€.

public class ExecutorServiceExample {
    public <T> T submitTask(Callable<T> task) throws Exception {
        System.out.println("Callable version called");
        return task.call();
    }

    public void submitTask(Runnable task) {
        System.out.println("Runnable version called");
        task.run();
    }

    public static void main(String[] args) throws Exception {
        ExecutorServiceExample service = new ExecutorServiceExample();

        // Runnable둜 전달
        service.submitTask(() -> System.out.println("Running task..."));

        // Callable둜 전달
        String result = service.submitTask(() -> {
            System.out.println("Calling task...");
            return "Task result";
        });
        System.out.println("Result: " + result);
    }
}

μ΄λ ‡κ²Œ λ©”μ„œλ“œ 이름을 λͺ…ν™•νžˆ λ‚˜λˆ„μ–΄ 각 λ©”μ„œλ“œκ°€ μ–΄λ–€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό λ°›λŠ”μ§€ λͺ…ν™•νžˆ ν•˜λ©΄ ν˜Όλž€μ„ 쀄일 수 μžˆλ‹€.

핡심 μš”μ•½

  1. ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 톡해 λžŒλ‹€ ν‘œν˜„μ‹μ„ ν™œμš©ν•˜λ©΄ μ½”λ“œκ°€ κ°„κ²°ν•΄μ§€κ³  μœ μ—°μ„±μ΄ λ†’μ•„μ§„λ‹€.
  2. ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 적극적으둜 ν™œμš©ν•˜κ³ , ν•„μš”ν•  λ•Œλ§Œ μ»€μŠ€ν…€ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•˜λŠ” 것이 μ’‹λ‹€.

Predicate, Function, Supplier, Consumer λ“±μ˜ μ£Όμš” ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λŠ” λŒ€λΆ€λΆ„μ˜ 경우λ₯Ό 컀버할 수 μžˆμ–΄, ν‘œμ€€ μΈν„°νŽ˜μ΄μŠ€ μ‚¬μš©μ„ μš°μ„ μ μœΌλ‘œ κ³ λ €ν•΄μ•Ό ν•œλ‹€.

  1. μ»€μŠ€ν…€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ •μ˜ν•  λ•ŒλŠ” λ°˜λ“œμ‹œ 따라야 ν•  κ·œμ•½μ΄λ‚˜ μœ μš©ν•œ λ””ν΄νŠΈ λ©”μ„œλ“œκ°€ ν•„μš”ν•œμ§€ 고민해보아야 ν•œλ‹€.
  2. ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 닀쀑 μ •μ˜ν•˜λŠ” 것을 ν”Όν•˜μ—¬ λͺ¨ν˜Έν•¨μ„ λ°©μ§€ν•œλ‹€.
  3. μžλ°”μ—μ„œ ν‘œμ€€ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€λ₯Ό 적극 ν™œμš©ν•˜λ©΄ λΆˆν•„μš”ν•œ μΈν„°νŽ˜μ΄μŠ€ μž‘μ„±μ„ 쀄일 수 있으며, APIκ°€ 더 일관성 있고 κ°„κ²°ν•΄μ§„λ‹€.
  4. κΈ°λ³Έ νƒ€μž…μš© μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ μ„±λŠ₯을 μ΅œμ ν™”ν•  수 μžˆμŠ΅λ‹ˆλ‹€. λ°•μ‹±λœ νƒ€μž… λŒ€μ‹  IntPredicate, LongFunction λ“± κΈ°λ³Έ νƒ€μž…μ„ μœ„ν•œ μΈν„°νŽ˜μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜λŠ” 것이 μ’‹λ‹€.

μžλ°”μ—μ„œλ„ ν•¨μˆ˜ν˜• ν”„λ‘œκ·Έλž˜λ°μ˜ μž₯점을 ν™œμš©ν•  수 μžˆμœΌλ―€λ‘œ, API 섀계 μ‹œ ν•¨μˆ˜ν˜• μΈν„°νŽ˜μ΄μŠ€μ˜ ν™œμš©μ„ 염두에 λ‘λŠ” 것이 μ’‹λ‹€.