GENERIC CONTRACTS

제네릭 클래스 인터페이스 계약

제네릭을 여러 타입에서 재사용한다는 설명만으로는 설계 기준이 부족하다. 클래스의 상태가 어떤 T를 저장하는지, 인터페이스의 메서드들이 같은 T를 공유해야 하는지, 제약 조건이 내부 구현을 얼마나 안전하게 만드는지 봐야 한다.

01

변하는 타입 찾기

구조는 같고 값 타입만 달라지는 위치를 타입 매개변수로 분리한다.

무엇이 변하지 않는지도 중요하다
02

클래스 상태 연결

클래스가 저장하는 값과 반환하는 값이 같은 T를 공유해야 하는지 확인한다.

생성자와 메서드가 같은 계약을 본다
03

인터페이스 계약 정의

읽기, 쓰기, 비교, 변환 같은 메서드가 T를 어떻게 입력과 출력에 쓰는지 명시한다.

메서드마다 독립 제네릭인지 타입 전체 제네릭인지 나눈다
04

제약 추가

내부에서 id, length, key 같은 속성을 쓰려면 extends로 최소 조건을 둔다.

제약 없는 T에서는 아무 속성도 안전하게 읽을 수 없다
05

과한 일반화 줄이기

한 번만 쓰거나 추론이 어려운 제네릭은 구체 타입이나 overload가 더 읽기 쉬운지 본다.

제네릭은 복잡도를 빌려오는 도구다
Class<T>
상태 타입 고정 인스턴스가 생성될 때 저장 타입을 정하고 메서드가 같은 타입을 유지한다.
Repository<User>처럼 읽힌다
Interface<T>
계약 타입 공유 구현체가 어떤 타입을 받고 돌려주는지 외부에 고정한다.
DI와 mock에도 도움이 된다
extends
최소 조건 T가 가져야 할 속성과 메서드를 제한해 내부 구현을 안전하게 한다.
너무 강한 제약은 재사용성을 줄인다
default type
기본 타입 인자 자주 쓰는 타입을 기본값으로 두어 사용성을 높인다.
추론 실패 시 의도치 않은 기본값을 확인한다

타입 확인

추론 결과 호출자가 타입 인자를 생략했을 때 원하는 타입으로 추론되는지 확인한다.
제약 필요 내부에서 쓰는 속성이 extends 제약으로 표현되어 있는지 본다.
any 탈출 제네릭이 어려워 any로 빠지는 부분이 없는지 확인한다.