Skip to content

아이템 44. 표준 함수형 인터페이스를 사용하라 - version 1.0 #105

Closed
@Irisation23

Description

@Irisation23

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()));
    }
}

실행 결과는 다음과 같다.
image


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);
  }
}
// 나머지 동일

1.1 표준 함수형 인터페이스의 종류

1.2 표준 함수형 인터페이스의 도입

1.3 표준 함수형 인터페이스 도입의 예외


2. 핵심 정리 📚


3. 회고 🧹

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions