[Effective Java] - 5장 아이템 30. 이왕이면 제네릭 메서드로 만들라

yeom yaloo·2023년 12월 26일
0

Effective Java

목록 보기
18/20
post-thumbnail

제네릭

[아이템 30. 이왕이면 제네릭 메서드로 만들라]

[핵심 정리]

  • 클라이언트에서 매개변수와 반환값을 명시적으로 형변환해야 하는 메서드보다 제네릭 메서드가 더 안전하고 사용하기도 쉽다.
  • 메서드도 형변환 없이 사용할 수 있는 것이 훨씬 좋고 그렇게 사용하고자 한다면 제네릭 메서드로 만들어 사용하면 된다.
  • 타입 역시 마찬가지로 형변환을 해줘야하는 기존 메서드는 제네릭으로 만들자

[제네릭 메서드?]

1. 제네릭 메서드란?

  • 클래스를 제네릭 클래스로 만들 수 있다.
  • public class Box<T>{ ... } 형식이 제네릭 클래스라고 할수 있다.
  • 메서드 역시 제네릭으로 만들 수 있다.
    • public static Set<E>
  • 매개변수화 타입을 받는 정적 유틸리티 메서드는 보통 제네릭이다.

2. 제네릭을 이용한 타입 안전을 챙긴 메서드 만들기

2-1. 로타입을 사용한 방식의 문제점

public static Set union(Set s1, Set s2){
	Set result = new HashSet(s1);
	result.addAll(s2);
	return result;
}
  • 위의 로 코드를 사용한 코드는 컴파일은 되지만 비검사 경고 두개가 발생한다.
  • 이를 없애기 위해서는 메서드 타입을 안전하게 만들어야 한다.

2-2. 코드 수정

  1. 메서드 선언에서 세 집합의 원소 타입을 타입 매개변수로 명시하자.
  2. 메서드 안에서 이 타입 매개변수만 사용하게 수정하자.
public static <E> Set<E> union(Set<E> s1, Set<E> s2){ // 메서드 선언에서 세 집합의 원소 타입을 타입 매개변수로 명시
	
    Set<E> result = new HashSet(s1);// 메서드 안에서 이 타입 매개변수만을 사용하게 수정
    
	result.addAll(s2);
    
	return result;
}
  • 위의 코드로 수정하게 되면 경고 없이 컴파일이 되고 타입 안전하고 쓰기에도 쉬워진다.
  • 위에서 <E>를 사용해줌으로 제네릭 메서드를 선언하고 있습니다. 이것은 메서드에 대한 제네릭 타입 매개변수를 나타냅니다.

3. public static Set<String> union()과 차이?

  • 해당 메서드는 Set<String>이라는 반환 타입을 정의하고 있을 뿐 제네릭 사용에 대한 처리는 없다.
  • 이 경우라면 메서드 자체에서 제네릭 타입을 활용하지 않는 단순 반환형을 가질 뿐이다.

[불변 객체를 여러 타입으로 활용]

1. 런타임에 타입 정보가 소거되는 제네릭

  • 제네릭의 경우엔 런타임시에 타입 정보가 소거되기 때문에 하나의 객체를 어떤 타입으로든 매개변수화할 수 있다.
    • 매개변수화? ex) List<T>
  • 이때 이를 위해서는 매번 그 객체의 타입을 바꿔주는 정적 팩터리를 만들어야 한다.
    • 이 패턴을 제네릭 싱글턴 팩터리라고 한다.
    • Collections.reverseOrder 같은 함수 객체나 Collections.emptySet 같은 컬렉션 용으로 사용한다.

2. 제네릭 싱글턴 팩터리 패턴

2-1. 싱글턴 팩터리 패턴

public class SingletonFactory {
    private static final SingletonFactory instance = new SingletonFactory();

    private SingletonFactory() {
        // private 생성자로 외부에서 직접 인스턴스 생성을 막음
    }

    public static SingletonFactory getInstance() {
        return instance;
    }

    public SomeSingletonObject createSingletonObject() {
        // 여기서 단일 객체를 생성하거나 반환하는 로직을 구현할 수 있음
        return SomeSingletonObject.getInstance();
    }
}

class SomeSingletonObject {
    private static final SomeSingletonObject instance = new SomeSingletonObject();

    private SomeSingletonObject() {
        // private 생성자로 외부에서 직접 인스턴스 생성을 막음
    }

    public static SomeSingletonObject getInstance() {
        return instance;
    }

    public void doSomething() {
        // 필요한 동작 구현
    }
}

2-2. 제네릭 싱글턴 팩터리 패턴

public class Factory<T> {
    private Map<Class<? extends T>, Supplier<? extends T>> map = new HashMap<>();

    private Factory() {
        // private 생성자로 외부에서 직접 인스턴스 생성을 막음
    }

    public static <T> Factory<T> create() {
        return new Factory<>();
    }

    public <E extends T> void register(Class<E> clazz, Supplier<E> supplier) {
        map.put(clazz, supplier);
    }

    public <E extends T> E createObject(Class<E> clazz) {
        Supplier<? extends T> supplier = map.get(clazz);
        if (supplier == null) {
            throw new IllegalArgumentException("Class not registered: " + clazz);
        }
        return clazz.cast(supplier.get());
    }
}
  • 해당 방법은 타입의 객체를 생성하는 공통 로직을 단일한 방식으로 처리하고자 할 때 유용하기 때문에 제네릭을 이용해서 만들어 사용한다.
profile
즐겁고 괴로운 개발😎

0개의 댓글