🎯 Java 인터페이스 완벽 가이드
"구현이 아닌 인터페이스에 의존하라" - 객체지향 설계의 핵심 원칙
📋 목차
1️⃣ ArrayList vs LinkedList: 왜 두 가지일까?
🤔 흔한 고민
JCF(Java Collections Framework)를 사용하다 보면 이런 의문이 듭니다:
- 왜 자바는 List 인터페이스에 두 가지 구현을 제공할까?
ArrayList와LinkedList중 어느 것을 선택해야 할까?- 둘의 차이가 정확히 뭘까?
💡 핵심 답변
두 구현체는 각각의 장단점이 명확합니다:
| 상황 | 최적의 선택 |
|---|---|
| 🔍 랜덤 접근이 많은 경우 | ArrayList ✅ |
| ➕ 빈번한 삽입/삭제 | LinkedList ✅ |
| 💾 메모리 효율 중시 | 상황에 따라 다름 |
✨ 핵심 포인트: 어느 것이 더 좋을지는 수행하는 동작에 달려 있습니다!
2️⃣ Java Interface의 본질
📖 Interface란?
자바 interface는 메서드 집합의 명세입니다.
이 interface를 구현하는 클래스는 명세된 모든 메서드를 반드시 제공해야 합니다.
🔍 실전 예제: Comparable Interface
public interface Comparable<T> {
public int compareTo(T o);
}
✅ Interface 구현 조건
- 타입 파라미터 T를 명시해야 합니다
T타입의 객체를 인자로 받고int를 반환하는 compareTo() 메서드를 제공해야 합니다
💼 실제 구현 사례: Integer 클래스
public final class Integer extends Number implements Comparable<Integer> {
public int compareTo(Integer anotherInteger) {
int thisVal = this.value;
int anotherVal = anotherInteger.value;
// 삼항 연산자를 활용한 비교 로직
return (thisVal < anotherVal ? -1 : (thisVal == anotherVal ? 0 : 1));
}
// ... 다른 메서드들
}
🔑 이 코드의 핵심
Number클래스를 상속 (extends)Comparable<Integer>인터페이스를 구현 (implements)- Integer 객체를 받아 int를 반환하는
compareTo()메서드 제공
🛡️ 컴파일러의 역할
클래스가 interface를 구현한다고 선언하면, 컴파일러는 자동으로 검증합니다:
- ✅ interface가 정의한 모든 메서드를 제공하는가?
- ✅ 메서드 시그니처가 정확히 일치하는가?
💡 Tip: compareTo() 메서드 구현 시 삼항 연산자(ternary operator)
? :를 자주 사용합니다!
3️⃣ List Interface 완전 정복
🎨 List Interface의 구조
JCF는 다음과 같은 구조를 제공합니다:
List (Interface)
├── ArrayList (구현 클래스)
└── LinkedList (구현 클래스)
📝 List Interface의 역할
List interface는 **"List가 된다는 의미"**를 정의합니다.
구현 클래스는 다음을 제공해야 합니다:
add()- 요소 추가get()- 요소 조회remove()- 요소 제거- 그 외 약 20가지 메서드
🔄 상호 교환 가능성
ArrayList와 LinkedList는 동일한 interface를 구현하므로 완벽하게 상호교환 가능합니다!
// 이 메서드는 ArrayList든 LinkedList든 잘 동작합니다
public void processData(List<String> data) {
data.add("new item");
String first = data.get(0);
data.remove(first);
}
💻 실전 예제: ListClientExample
public class ListClientExample {
// ⭐ 핵심: 구체적인 구현 클래스가 아닌 Interface 타입으로 선언
private List list;
// 생성자에서만 구체적인 구현체를 지정
public ListClientExample() {
list = new LinkedList(); // 여기만 바꾸면 ArrayList로 전환 가능!
}
// 반환 타입도 Interface로!
private List getList() {
return list;
}
public static void main(String[] args) {
ListClientExample lce = new ListClientExample();
List list = lce.getList();
System.out.println(list);
}
}
🎯 이 코드의 설계 포인트
- 인스턴스 변수:
Listinterface 타입으로 선언 - 반환 타입:
getList()메서드도List타입 반환 - 구체 클래스 사용: 오직 생성자에서만!
🔄 유연한 구현체 교체
ArrayList로 바꾸고 싶다면? 생성자 한 줄만 수정하면 됩니다:
// Before
list = new LinkedList();
// After
list = new ArrayList(); // 이것만 바꾸면 끝!
4️⃣ 인터페이스 기반 프로그래밍
🎓 Interface-Based Programming이란?
인터페이스 기반 프로그래밍(Interface-Based Programming)은 다음을 의미합니다:
코드는 구체적인 구현 클래스가 아닌 인터페이스에만 의존해야 한다
✅ 올바른 방식
// ✅ GOOD: Interface에 의존
List<String> names = new ArrayList<>();
// ✅ GOOD: 메서드 파라미터도 Interface
public void processNames(List<String> names) {
// ...
}
❌ 피해야 할 방식
// ❌ BAD: 구체적인 구현 클래스에 의존
ArrayList<String> names = new ArrayList<>();
// ❌ BAD: 메서드 파라미터도 구현 클래스
public void processNames(ArrayList<String> names) {
// LinkedList를 전달할 수 없게 됨!
}
🎁 인터페이스 프로그래밍의 이점
1️⃣ 유연성: 구현체 교체가 쉬움
// 요구사항 변경 시 한 곳만 수정
// list = new LinkedList<>(); // 기존
list = new ArrayList<>(); // 변경
2️⃣ 재사용성: 다양한 구현체에서 동작
// 같은 코드가 모든 List 구현체에서 작동
public int getSize(List<?> items) {
return items.size();
}
3️⃣ 테스트 용이성: Mock 객체 사용 가능
// 테스트 시 가짜 구현체로 대체 가능
List<String> mockList = mock(List.class);
⚠️ 중요한 트레이드오프
| 대상 | 변경의 영향 |
|---|---|
| 🔄 구현 클래스 변경 | ✅ 클라이언트 코드에 영향 없음 |
| ⚡ 인터페이스 변경 | ❌ 모든 클라이언트 코드 수정 필요 |
📌 그래서: 라이브러리 개발자들은 꼭 필요한 경우가 아니면 인터페이스를 절대 변경하지 않습니다!
📚 핵심 정리
🎯 반드시 기억할 3가지
-
인터페이스에 의존하라
- 구체 클래스가 아닌
List,Set,Map같은 interface 사용
- 구체 클래스가 아닌
-
생성자에서만 구체화하라
new ArrayList<>(),new LinkedList<>()등은 생성 시점에만
-
변경에 유연한 코드를 작성하라
- 구현체 교체가 쉬운 구조 유지
💼 실무 적용 팁
// ✅ 추천하는 패턴
public class OrderService {
private final List<Order> orders; // Interface로 선언
public OrderService() {
this.orders = new ArrayList<>(); // 생성자에서만 구체화
}
public List<Order> getOrders() { // 반환도 Interface
return orders;
}
public void addOrder(Order order) {
orders.add(order);
}
}
🚀 다음 단계
- ArrayList와 LinkedList의 성능 차이 분석
- 다른 Collection Interface들 (Set, Map) 학습
- 커스텀 Interface 설계 연습
- 디자인 패턴에서의 Interface 활용
"좋은 코드는 변경에 유연하다. 인터페이스는 그 유연성의 핵심이다." 🎯