64. 객체는 인터페이스를 사용해 참조하라
주제: 객체는 인터페이스를 사용해 참조하라
(개념) 객체를 담는 변수, 필드, 메서드 매개변수, 반환 타입 등을 선언할 때, 실제 객체의 구체 클래스 타입 대신 해당 객체가 구현한 인터페이스 타입을 사용하라는 원칙.
(확장) 아이템 51 "매개변수 타입으로 인터페이스 사용" 원칙을 프로그램 전반으로 확장.
핵심 원칙
참조는 인터페이스로 (Reference via Interface)
생성만 클래스로 (Instantiate with Class)
// 좋은 예: 인터페이스로 참조, 클래스로 생성 List<User> users = new ArrayList<>(); Map<String, Data> dataMap = new HashMap<>(); MemberService service = new MemberServiceImpl(); // 나쁜 예: 구체 클래스로 참조 (유연성 저하) ArrayList<User> users2 = new ArrayList<>();
장점
graph LR
A[Client Code] --> B(Service Interface);
B -- impl --> C[ServiceImpl_A];
B -- impl --> D[ServiceImpl_B];
B -- impl --> E[ServiceImpl_C];
유연성 향상 (Increased Flexibility)
구현 교체 용이 (Easy Implementation Swapping)
테스트 용이성 & DI 활용 (Improved Testability & DI Usage)
@Component // 또는 @Controller, @Service 등 public class MyComponent { private final MemberService memberService; // ★ 인터페이스 타입으로 주입받음 @Autowired // 생성자 주입 권장 public MyComponent(MemberService memberService) { this.memberService = memberService; } public void doSomething() { // memberService의 실제 구현이 MemberServiceImpl이든, // NewMemberServiceImpl이든 이 코드는 변경 불필요 memberService.someMethod(); } }
주의점 (Potential Issues)
암묵적 동작 의존 (Dependency on Implicit Behavior)
LinkedHashSet -> HashSet
컴파일 시점 오류 (Compile-time Errors)
ArrayList<String> names = new ArrayList<String>(); //List<String> names = new LinkedList<>(); names.trimToSize(); // 컴파일 에러! List 인터페이스 및 LinkedList에는 해당 메서드 없음
예외
적합한 인터페이스 부재 (No Suitable Interface)
값 클래스 (
Value Classes
)String
,Integer
,BigInteger
등. 클래스 타입 직접 사용
클래스 기반 프레임워크 (Class-based Frameworks)
java.io.InputStream
,OutputStream
void processStream(InputStream is) throws IOException { int data; while ((data = is.read()) != -1) { // ... 데이터 처리 ... } is.close(); } // 사용 예시 InputStream fileInput = new FileInputStream("data.txt"); processStream(fileInput); byte[] memoryData = ...; InputStream memoryInput = new ByteArrayInputStream(memoryData); processStream(memoryInput);
구체 클래스 고유 메서드 필수 사용 시 (Need for Class-specific Methods)
ArrayList
의 TrimToSize()PriorityQueue
의comparator()
예외 상황 대처 (Handling Exceptions)
Trade-off 인지 (Acknowledge Trade-off) (유연성 < 특정 기능 이점)
대처 방안 (Choose Your Approach)
구체 클래스 타입 직접 선언 (Declare Concrete Class Type)
instanceof
+ 캐스팅 (Useinstanceof
+ Casting)
if (users instanceof ArrayList) { ((ArrayList<User>) users).ensureCapacity(1000); // ArrayList 고유 메서드 사용 }
최종 요약 (Key Takeaway)
"특별한 이유(고유 메서드 필수 사용 등)가 없다면, 항상 가장 추상적인 타입(인터페이스 > 상위 클래스)으로 참조하라. 특정 구현의 이점이 유연성보다 명확히 중요할 때만 예외를 고려하라."
Last updated