Closed
Description
Discussed in https://github.com/orgs/Study-2-Effective-Java/discussions/101
Originally posted by bunsung92 February 3, 2023
📝 구성
0. 들어가기에 앞서 🤔
자바가 람다를 지원하면서 API를 작성하는 모범 사례도 크게 바뀌었다.
즉 상위 클래스의 기본 메서드를 재정의해 원하는 동작을 구현하는 템플릿 메서드 패턴
에서 함수 객체를 받는 정적 팩터리나 생성자를 제공하는 방식으로 바뀌게 되었다.
일반화 해보자면 함수 객체를 매개변수로 받는 생성자와 메서드를 더 많이 만들어야 한다는 말이다.
중요한 점은 매개변수의 함수 객체 타입을 올바르게 선택해야 한다.
1. Version1 removeEldestEntry() 구현하기 - Basic
LinkedHashMap
을 이용하여 캐시
를 구현한다고 생각해보자.
캐시란? 🧐
값비싼 연산 결과 또는 자주 참조되는 데이터를 메모리 안에 두고, 뒤이은 요청이 보다 빨리 처리될 수 있도록 하는 저장소이다.
removeEldestEntry()
를 @Override
하면 캐시로 사용할 수 있다.
- 해당 메서드가 캐시로 이용될 수 있는 이유 Map 객체가 캐시로 사용될 때 이 메서드를 재정의하여, 새로운 엔트리가 추가될 때마다
가장 오래된 엔트리를 제거하여 Map 객체가 적절한 크기를 유지할 수 있기 때문이다.
@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
직접 구현해 보자
먼저 MyCache 제네릭 클래스
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class MyCache<K, V> extends LinkedHashMap<K, V> {
private final int limitSize;
public MyCache(int limitSize) {
this.limitSize = limitSize;
}
@Override
protected boolean removeEldestEntry(Entry<K, V> eldest) {
return size() > this.limitSize;
}
}
Main 클래스
public class Main {
public static void main(String[] args) {
MyCache<String, Integer> cache = new MyCache<>(3);
cache.put("1", 1);
cache.put("2", 2);
cache.put("3", 3);
cache.put("4", 4);
System.out.println(String.join(", ", cache.keySet()));
}
}
2. Version2 removeEldestEntry() 구현하기 - @FunctionalInterface
MyCache2 제네릭 클래스
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class MyCache2<K, V> extends LinkedHashMap<K, V> {
private final EldestEntryRemovalFunction<K, V> eldestEntryRemovalFunction;
public MyCache2(EldestEntryRemovalFunction<K, V> eldestEntryRemovalFunction) {
this.eldestEntryRemovalFunction = eldestEntryRemovalFunction;
}
@Override
protected boolean removeEldestEntry(Entry<K, V> eldest) {
return eldestEntryRemovalFunction.remove(this, eldest);
}
}
EldestEntryRemovalFunction 인터페이스
import java.util.Map;
public interface EldestEntryRemovalFunction<K, V> {
boolean remove(Map<K, V> map, Map.Entry<K, V> eldest);
}
main 메서드 - 달라진 부분만 기록
MyCache2<String, Integer> cache2 = new MyCache2<>(((map, eldest) -> map.size() > 3));
// 이하 동일
자 이렇게 함수형 인터페이스를 직접 정의해서 사용할 수 있다.
하지만 EldestEntryRemovalFunction<K, V>
인터페이스의 구조는 이미 표준 자바 함수형 인터페이스에서 정의하고 있다.
3. Version3 removeEldestEntry() 구현하기 - 표준 함수형 인터페이스 ✨
MyCache3 제네릭 클래스
public class MyCache3<K, V> extends LinkedHashMap<K, V> {
private final BiPredicate<Map<K, V>, Map.Entry<K, V>> biPredicate;
public Cache(BiPredicate<Map<K, V>, Map.Entry<K, V>> biPredicate) {
this.biPredicate = biPredicate;
}
@Override
protected boolean removeEldestEntry(Entry<K, V> eldest) {
return biPredicate.test(this, eldest);
}
}
// 나머지 동일