3주차 Unit 4.4 — 추상클래스 vs 인터페이스 선택 기준

Psj·2026년 5월 19일

F-lab

목록 보기
91/240

Unit 4.4 — 추상클래스 vs 인터페이스 선택 기준

F-LAB JAVA · 3주차 · Phase 4 · 추상화의 두 도구
🏆 Phase 4 완주 — 추상화 마스터 달성


📌 학습 목표

이 Unit을 끝내면 다음을 답할 수 있어야 한다.

  • "is-a" 관계 vs "can-do" 능력 의 의미는?
  • 5가지 결정 기준 으로 두 도구 중 선택할 수 있나?
  • 자바 표준 라이브러리 의 두 도구 선택 분석은?
  • Spring/JPA 의 추상화 선택은?
  • AbstractList + List 패턴 의 의미는?
  • 사용자 정의 추상화 설계 가이드는?
  • Sealed 인터페이스/클래스 (Java 17+) 의 위치는?
  • Phase 4 의 30가지 졸업 시험 통과 가능?

🎯 핵심 한 문장

추상클래스는 "강한 is-a 관계 + 공통 상태", 인터페이스는 "유연한 can-do 능력 + 다중 분류" 의 도구다.
두 도구가 자주 함께 사용되는 패턴 — AbstractList implements List — 가 자바의 표준이다.
인터페이스로 외부 API 명세, 추상클래스로 공통 구현 제공.
Spring 의 JpaRepository(인터페이스) + SimpleJpaRepository(추상/구체) 가 대표적.
둘은 경쟁이 아닌 상호 보완 도구 이며, 좋은 설계는 둘을 조합한다.

비유 — 자격증과 본업의 조합

인터페이스 (자격증):
  "수영 가능 (Swimmer)" 자격증
  "비행 가능 (Flyer)" 자격증
  → 누구나 따고 발휘
  → 본업과 무관

추상클래스 (직업의 기본):
  "회사원 (Employee)" 의 기본 책임
  - 출근, 보고서 작성 (공통)
  - 업무 (직무별 다름)
  → 직장에 속함 (단일)

조합:
  Person extends Employee implements Swimmer, Flyer
  → 직업 + 자격증들 = 완전한 사람

→ 추상클래스 = 본업, 인터페이스 = 능력 자격증.


🧭 9개 섹션 로드맵

1. 5가지 결정 기준
2. is-a vs can-do 의 깊은 의미
3. 자바 표준 라이브러리의 선택 분석
4. AbstractList + List 패턴
5. Spring/JPA 의 추상화
6. 사용자 정의 추상화 설계 가이드
7. Sealed 인터페이스/클래스 (Java 17+)
8. Phase 4 졸업 시험 — 30 문항
9. Phase 4 완주 + Phase 5 진입

1️⃣ 5가지 결정 기준

1.1 결정 기준 종합표

기준인터페이스추상클래스
관계 유형can-do (능력)is-a (분류)
다중성다중 구현 ✓단일 상속만
상태 보유❌ (필드 X)✓ (필드 가능)
공통 구현default 메서드만모든 구현 가능
확장 의도능력 부여골격 제공

1.2 기준 1 — 관계의 본질

"is-a" 관계 강한가? → 추상클래스
  - Dog is-a Animal
  - Circle is-a Shape
  - ArrayList is-a AbstractList
  
"can-do" 능력만 표현? → 인터페이스
  - Bird can-do Flying
  - Object can-do Comparable
  - Resource can-do AutoCloseable

판단:

  • "X 는 Y 의 한 종류" → 추상클래스
  • "X 는 Y 를 할 수 있음" → 인터페이스

1.3 기준 2 — 다중 분류 필요?

// 다중 분류 필요 시 → 인터페이스 필수
public class Duck implements Animal, Swimmer, Flyer {
    // 동물 + 수영 + 비행
}

// 단일 분류 OK → 추상클래스 가능
public class Bird extends Animal {
    // 동물 한 분류
}

판단:

  • 한 클래스가 여러 분류에 속함 → 인터페이스
  • 단일 분류면 충분 → 추상클래스 가능

1.4 기준 3 — 공통 상태 (필드) 필요?

// 공통 상태 → 추상클래스
public abstract class Animal {
    protected String name;     // 모든 동물 공통
    protected int age;          // 모든 동물 공통
    
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

// 상태 없이 능력만 → 인터페이스
public interface Drawable {
    void draw();
    void erase();
    // 상태 X
}

판단:

  • 모든 자식이 같은 필드 보유 → 추상클래스
  • 메서드 명세만 → 인터페이스

1.5 기준 4 — 공통 구현 제공?

// 복잡한 공통 구현 → 추상클래스
public abstract class AbstractList<E> implements List<E> {
    // 수많은 공통 구현 메서드들
    public boolean contains(Object o) { ... }
    public Iterator<E> iterator() { ... }
    public boolean equals(Object o) { ... }
    public int hashCode() { ... }
    // 자식은 get(int) + size() 만 구현
}

// 단순한 공통 동작만 → 인터페이스 default
public interface Drawable {
    void draw();
    
    default void drawTwice() {
        draw();
        draw();
    }
}

판단:

  • 복잡한 공통 로직 → 추상클래스
  • 간단한 편의 메서드 → default
  • 상태 의존 → 추상클래스

1.6 기준 5 — 확장 의도

추상클래스 의도:
  - "이 골격을 따라 확장하라"
  - Template Method 패턴
  - 강한 결합, 안정적 확장

인터페이스 의도:
  - "이 능력을 자유롭게 구현하라"
  - 다양한 구현체 가능
  - 약한 결합, 유연한 확장

판단:

  • 골격 통일 + 확장 지점 강제 → 추상클래스
  • 능력만 표현 + 자유로운 구현 → 인터페이스

1.7 결정 트리

설계 시 의사결정 트리:

Step 1: 다중 분류 필요?
  YES → 인터페이스 (필수)
  NO  → Step 2

Step 2: 강한 is-a 관계?
  YES → 추상클래스 후보
  NO  → 인터페이스 후보

Step 3: 공통 상태/필드 필요?
  YES → 추상클래스
  NO  → Step 4

Step 4: 복잡한 공통 구현?
  YES → 추상클래스
  NO  → 인터페이스 (default 활용)

Step 5: 둘 다 필요?
  → 결합! AbstractX implements X

1.8 5가지 기준 활용 예시

// 시나리오 1: 도형
// - is-a 강함 (Circle is-a Shape)
// - 공통 상태 (color, position)
// - 단일 분류로 충분
→ 추상클래스 Shape

// 시나리오 2: 직렬화
// - 능력만 표현
// - 다중 부여 가능
// - 상태 없음
→ 인터페이스 Serializable

// 시나리오 3: 운송수단
// - is-a 강함 (Car is-a Vehicle)
// - 공통 상태 (wheels, capacity)
// - 다중 능력 (Drivable, Fuelable)
→ 추상클래스 Vehicle + 인터페이스 Drivable, Fuelable

// 시나리오 4: 음악 재생
// - 능력 표현 (Playable)
// - 다양한 매체에 적용
→ 인터페이스 Playable

1.9 자기 점검 답변

추상클래스 vs 인터페이스를 결정하는 가장 중요한 기준 하나는?

:

  • 다중 분류 필요 여부가 가장 결정적
  • 다중 분류 필요 → 인터페이스 (다중 상속 불가능한 단일 상속의 한계)
  • 단일 분류 + 강한 is-a + 상태 보유 → 추상클래스
  • 두 도구는 경쟁 이 아닌 상호 보완

→ 좋은 설계는 보통 둘 다 사용.


2️⃣ is-a vs can-do 의 깊은 의미

2.1 is-a 관계의 본질

"is-a" (강한 분류 관계):

  Dog is-a Animal
  → Dog 는 Animal 의 한 종류
  → 정체성의 일부

특징:
  - 변경 불가 (한번 Animal 의 자식이면 영원히)
  - 자식이 부모 의 모든 속성 보유
  - 단일 상속 (한 분류에만 속함)
  - 강한 결합

2.2 is-a 예시 분석

// 강한 is-a
abstract class Shape { ... }
class Circle extends Shape { ... }
// → Circle 은 영원히 Shape, 다른 분류로 못 옮김

// 모호한 is-a
class Bird extends Animal { ... }
class Penguin extends Bird { ... }
// → Penguin 은 Bird 인데, fly() 메서드는?
// → 분류 계층의 한계

→ is-a 가 명확하지 않으면 추상클래스 부적합.

2.3 can-do 능력의 본질

"can-do" (능력/행위):

  Bird can-do Fly
  → Bird 가 Fly 능력 보유
  → 분류와 독립

특징:
  - 추가/제거 가능 (능력 변경)
  - 분류와 무관
  - 다중 능력 가능
  - 약한 결합

2.4 can-do 예시

// 능력 분리
interface Swimmer { void swim(); }
interface Flyer { void fly(); }
interface Walker { void walk(); }

// 다양한 조합
class Duck implements Swimmer, Flyer, Walker { ... }
class Fish implements Swimmer { ... }
class Eagle implements Flyer, Walker { ... }
class Human implements Swimmer, Walker { ... }

// 능력은 자유롭게 조합

2.5 is-a 와 can-do 의 혼동

// ❌ 혼동된 설계 — Animal 에 swim() 추가
abstract class Animal {
    abstract void swim();   // 모든 동물이 수영?
}

class Cat extends Animal {
    @Override
    public void swim() {
        throw new UnsupportedOperationException("Cats don't swim");
    }
}

// 문제:
// - 일부 자식이 메서드를 부정 (LSP 위반)
// - 추상화가 잘못됨
// ✓ 올바른 분리
abstract class Animal {
    // 모든 동물 공통만
    abstract void eat();
    abstract void sleep();
}

interface Swimmer {
    void swim();
}

class Fish extends Animal implements Swimmer {
    @Override public void eat() { ... }
    @Override public void sleep() { ... }
    @Override public void swim() { ... }
}

class Cat extends Animal {
    @Override public void eat() { ... }
    @Override public void sleep() { ... }
    // swim() 안 받음 (Cat 은 Swimmer 아님)
}

분류 (추상클래스) 와 능력 (인터페이스) 의 분리.

2.6 Liskov Substitution Principle (LSP)

LSP (리스코프 치환 원칙):

  자식 클래스는 부모 클래스로 치환 가능해야.
  자식이 부모의 동작을 부정하면 안 됨.

위반 예:
  Bird → Penguin (Penguin.fly() 부정)
  Animal → Cat (Cat.swim() 부정)

해결:
  is-a 가 강한 곳에만 상속
  부정될 수 있는 능력은 인터페이스로 분리

2.7 자바 표준의 모범 사례

// 자바 표준의 좋은 예

// is-a (추상클래스)
abstract class Number { ... }
class Integer extends Number { ... }
class Double extends Number { ... }
// → 모두 숫자

// can-do (인터페이스)
interface Comparable<T> { int compareTo(T o); }
interface Iterable<T> { Iterator<T> iterator(); }
interface AutoCloseable { void close(); }

// 조합
class ArrayList<E>
    extends AbstractList<E>           // is-a
    implements List<E>,                // 명세
               RandomAccess,            // 능력 (마커)
               Cloneable,               // 능력 (마커)
               Serializable {           // 능력 (마커)
}

2.8 자기 점검 답변

is-a 관계와 can-do 능력의 본질적 차이는?

:

  • is-a (분류):

    • 정체성의 일부
    • 변경 불가
    • 단일 상속
    • "X 는 Y 의 한 종류"
  • can-do (능력):

    • 추가/제거 가능
    • 분류와 무관
    • 다중 가능
    • "X 는 Y 를 할 수 있음"

설계 원칙:

  • 분류는 추상클래스
  • 능력은 인터페이스
  • LSP 위반 회피
  • 능력을 분류로 강제하지 X

3️⃣ 자바 표준 라이브러리의 선택 분석

3.1 컬렉션의 이중 구조

컬렉션 프레임워크 (java.util):

인터페이스 (외부 명세):
  Iterable<E>
    └── Collection<E>
          ├── List<E>
          ├── Set<E>
          └── Queue<E>

추상클래스 (공통 구현):
  AbstractCollection<E>
    ├── AbstractList<E>
    │     └── ArrayList, LinkedList, ...
    ├── AbstractSet<E>
    │     └── HashSet, TreeSet, ...
    └── AbstractQueue<E>
          └── PriorityQueue, ...

핵심 패턴:

  • 인터페이스 = 사용자가 의존하는 타입
  • 추상클래스 = 구현자가 활용하는 토대
  • 구체 클래스 = 실제 사용 클래스

3.2 Number 추상클래스

public abstract class Number {
    
    public abstract int intValue();
    public abstract long longValue();
    public abstract float floatValue();
    public abstract double doubleValue();
    
    // 기본 구현
    public byte byteValue() {
        return (byte) intValue();
    }
    
    public short shortValue() {
        return (short) intValue();
    }
}

// 자식들
public class Integer extends Number {
    private final int value;
    
    @Override public int intValue() { return value; }
    @Override public long longValue() { return value; }
    // ...
}

public class Long extends Number { ... }
public class Float extends Number { ... }
public class Double extends Number { ... }
public class BigInteger extends Number { ... }
public class BigDecimal extends Number { ... }

분석:

  • is-a 강함: Integer is-a Number
  • 공통 메서드: byteValue(), shortValue() 의 기본 구현
  • 다중 분류 불필요: 숫자는 단일 분류
  • → 추상클래스 적합

3.3 Comparable 인터페이스

public interface Comparable<T> {
    int compareTo(T o);
}

// 구현
class Integer implements Comparable<Integer> {
    @Override
    public int compareTo(Integer other) {
        return Integer.compare(this.value, other.value);
    }
}

class String implements Comparable<String> { ... }
class LocalDate implements Comparable<LocalDate> { ... }

분석:

  • can-do 능력: "비교 가능"
  • 다양한 타입에 적용: Integer, String, LocalDate, ...
  • 상태 없음: 메서드 명세만
  • → 인터페이스 적합

3.4 InputStream 추상클래스

public abstract class InputStream implements Closeable {
    
    // 추상 메서드
    public abstract int read() throws IOException;
    
    // 공통 구현
    public int read(byte[] b) throws IOException {
        return read(b, 0, b.length);
    }
    
    public int read(byte[] b, int off, int len) throws IOException {
        // read() 활용한 기본 구현
        for (int i = 0; i < len; i++) {
            int c = read();
            if (c == -1) return i;
            b[off + i] = (byte) c;
        }
        return len;
    }
    
    public long skip(long n) throws IOException { ... }
    public int available() throws IOException { ... }
}

// 자식들
public class FileInputStream extends InputStream { ... }
public class ByteArrayInputStream extends InputStream { ... }
public class FilterInputStream extends InputStream { ... }

분석:

  • is-a 강함: FileInputStream is-a InputStream
  • 공통 구현 제공: read(byte[]) 등을 read() 위에 구현
  • 자식이 read() 만 구현하면 됨
  • → 추상클래스 적합

→ Closeable 인터페이스도 구현 (능력).

3.5 AutoCloseable 인터페이스

public interface AutoCloseable {
    void close() throws Exception;
}

// 다양한 구현
class FileInputStream extends InputStream implements AutoCloseable { ... }
class Connection implements AutoCloseable { ... }
class Statement implements AutoCloseable { ... }
class Stream<T> implements AutoCloseable { ... }

분석:

  • can-do 능력: "닫을 수 있음"
  • 다양한 클래스에 적용: 파일, DB 연결, 스트림 등
  • try-with-resources 와 결합
  • → 인터페이스 적합

3.6 자바 표준의 패턴 정리

도구자바 표준 예이유
추상클래스AbstractList, Number, InputStream, AbstractMap, HttpServlet강한 is-a + 공통 구현
인터페이스Comparable, Iterable, AutoCloseable, Runnable능력 표현 + 다중 부여
둘 다ArrayList(extends + implements)분류 + 능력

3.7 자바 8+ 의 변화

Java 8+ 의 진화:

이전:
  - 추상클래스: 공통 구현
  - 인터페이스: 명세만

Java 8+:
  - 추상클래스: 공통 구현 + 상태
  - 인터페이스: 명세 + default + static

선택 기준 변화:
  - 단순 편의 메서드 → 인터페이스 default (더 유연)
  - 복잡한 공통 로직 + 상태 → 추상클래스

3.8 Iterable + Iterator 분리

public interface Iterable<T> {
    Iterator<T> iterator();
    
    default void forEach(Consumer<? super T> action) { ... }
}

public interface Iterator<T> {
    boolean hasNext();
    T next();
    default void remove() { throw new UnsupportedOperationException(); }
}

분석:

  • Iterable: "순회 가능한 것"
  • Iterator: "순회 도구"
  • 두 개 인터페이스로 분리
  • 한 컬렉션이 여러 Iterator 생성 가능

→ Iterator 패턴의 표준 구현.

3.9 자기 점검 답변

자바 표준 라이브러리가 두 도구를 어떻게 조합하나?

:
1. 인터페이스 = 명세: List, Iterable, Comparable
2. 추상클래스 = 공통 구현: AbstractList, AbstractMap
3. 구체 클래스 = 실제: ArrayList, HashMap
4. 마커 인터페이스 = 능력: RandomAccess, Serializable
5. 표준 패턴: ArrayList extends AbstractList implements List, RandomAccess, Cloneable, Serializable

→ "외부 API 는 인터페이스, 내부 구현은 추상클래스, 능력은 인터페이스".


4️⃣ AbstractList + List 패턴

4.1 패턴의 구조

표준 패턴:

  interface List<E>            ← 외부 API
       ↑
       implements
       │
  abstract class AbstractList<E>  ← 공통 구현
       ↑
       extends
       │
  class ArrayList<E>             ← 구체 클래스

4.2 List 인터페이스 — 외부 API

public interface List<E> extends Collection<E> {
    
    int size();
    boolean isEmpty();
    boolean contains(Object o);
    boolean add(E e);
    boolean remove(Object o);
    
    E get(int index);
    E set(int index, E element);
    void add(int index, E element);
    E remove(int index);
    
    int indexOf(Object o);
    int lastIndexOf(Object o);
    
    Iterator<E> iterator();
    
    // Java 8+ default
    default void sort(Comparator<? super E> c) { ... }
    default void replaceAll(UnaryOperator<E> operator) { ... }
    
    // Java 9+ static
    static <E> List<E> of() { ... }
    static <E> List<E> of(E e1) { ... }
    static <E> List<E> copyOf(Collection<? extends E> coll) { ... }
}

역할:

  • 사용자 코드 의존: List<Shipment> list = ...
  • 다양한 구현체 호환: ArrayList, LinkedList, ...
  • 외부 명세: 어떤 동작인지 정의

4.3 AbstractList — 공통 구현

public abstract class AbstractList<E> 
        extends AbstractCollection<E> 
        implements List<E> {
    
    // 자식이 구현해야 할 핵심
    public abstract E get(int index);
    public abstract int size();
    
    // 공통 구현들
    
    public boolean add(E e) {
        add(size(), e);
        return true;
    }
    
    public void add(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    public E set(int index, E element) {
        throw new UnsupportedOperationException();
    }
    
    public E remove(int index) {
        throw new UnsupportedOperationException();
    }
    
    public int indexOf(Object o) {
        ListIterator<E> it = listIterator();
        while (it.hasNext()) {
            if (Objects.equals(o, it.next())) {
                return it.previousIndex();
            }
        }
        return -1;
    }
    
    public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    public ListIterator<E> listIterator() {
        return listIterator(0);
    }
    
    public boolean equals(Object o) {
        if (o == this) return true;
        if (!(o instanceof List)) return false;
        // List 의 표준 equals
        // ...
    }
    
    public int hashCode() {
        int hashCode = 1;
        for (E e : this) {
            hashCode = 31 * hashCode + (e == null ? 0 : e.hashCode());
        }
        return hashCode;
    }
    
    // 내부 Iterator 클래스
    private class Itr implements Iterator<E> {
        int cursor = 0;
        int lastRet = -1;
        int expectedModCount = modCount;
        
        public boolean hasNext() {
            return cursor != size();
        }
        
        public E next() {
            checkForComodification();
            int i = cursor;
            E next = get(i);
            lastRet = i;
            cursor = i + 1;
            return next;
        }
        // ...
    }
}

역할:

  • 공통 코드 재사용: indexOf, contains, equals, hashCode
  • 2개만 구현하면 List 완성: get(int) + size()
  • Iterator 내부 클래스 제공

4.4 ArrayList — 구체 클래스

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, Serializable {
    
    transient Object[] elementData;
    private int size;
    
    @Override
    public E get(int index) {
        Objects.checkIndex(index, size);
        return (E) elementData[index];
    }
    
    @Override
    public int size() {
        return size;
    }
    
    // 최적화된 add (AbstractList 의 기본보다 빠름)
    @Override
    public boolean add(E e) {
        ensureCapacity(size + 1);
        elementData[size++] = e;
        return true;
    }
    
    // 인덱스 기반 메서드도 최적화
    @Override
    public void add(int index, E element) {
        // ...
    }
    
    @Override
    public E remove(int index) {
        // ...
    }
}

역할:

  • 추상 메서드 구현: get, size
  • 성능 최적화: 자주 쓰는 메서드 직접 구현
  • 마커 인터페이스 추가: RandomAccess, Cloneable, Serializable

4.5 LinkedList — 또 다른 구체 클래스

public class LinkedList<E>
        extends AbstractSequentialList<E>   // ★ AbstractList 의 자식
        implements List<E>, Deque<E>, Cloneable, Serializable {
    
    transient int size = 0;
    transient Node<E> first;
    transient Node<E> last;
    
    @Override
    public E get(int index) {
        checkElementIndex(index);
        return node(index).item;
    }
    
    @Override
    public int size() {
        return size;
    }
    
    // 노드 기반 최적화
    // ...
}

특징:

  • AbstractSequentialList (AbstractList 의 자식) 상속
  • Deque 도 구현 (다중 인터페이스)
  • RandomAccess 안 함 (인덱스 느림)

4.6 사용자가 새 List 구현 시

// 사용자 정의 List — 매우 적은 코드로 가능
public class MyList<E> extends AbstractList<E> {
    
    private E[] data = (E[]) new Object[100];
    private int size = 0;
    
    @Override
    public E get(int index) {
        return data[index];
    }
    
    @Override
    public int size() {
        return size;
    }
    
    @Override
    public boolean add(E e) {
        data[size++] = e;
        return true;
    }
}

// MyList 가 List 의 모든 동작 자동 보유
MyList<String> list = new MyList<>();
list.add("a");
list.add("b");

// AbstractList 가 제공:
list.contains("a");    // ✓
list.indexOf("b");     // ✓
list.iterator();       // ✓
for (String s : list) { ... }   // ✓
list.equals(...);      // ✓
list.hashCode();       // ✓

→ AbstractList 의 강력함.

4.7 패턴의 효과

효과:

1. 외부 API 안정성 (인터페이스)
   - List 의 메서드 시그니처는 변하지 않음
   - 사용자 코드 안전

2. 공통 코드 재사용 (추상클래스)
   - AbstractList 가 90% 구현
   - 새 List 도 쉽게 추가

3. 성능 최적화 (구체 클래스)
   - ArrayList 가 자주 쓰는 메서드 오버라이드
   - 캐시 효율 등 활용

4. 다중 능력 부여 (마커 인터페이스)
   - RandomAccess, Serializable 등

4.8 같은 패턴의 다른 예

컬렉션 도메인:
  - List ← AbstractList ← ArrayList
  - Set ← AbstractSet ← HashSet
  - Map ← AbstractMap ← HashMap
  - Queue ← AbstractQueue ← PriorityQueue

I/O 도메인:
  - InputStream (추상클래스만) ← FileInputStream
    + Closeable 인터페이스
  - Reader ← AbstractReader 같은 패턴

웹:
  - Servlet ← GenericServlet ← HttpServlet
  - HttpServlet 자체가 추상클래스 + Servlet 인터페이스

JDBC:
  - Connection (인터페이스) ← drivers 가 직접 구현
  - PreparedStatement (인터페이스) ← drivers 의 구현

4.9 자기 점검 답변

AbstractList + List 패턴의 핵심 가치는?

:
1. 외부와 내부의 분리:

  • 외부 (사용자) 는 List 인터페이스에 의존
  • 내부 (구현자) 는 AbstractList 활용
  1. 코드 재사용:

    • AbstractList 가 indexOf, contains, equals 등 제공
    • 새 List 구현 시 2개 메서드만 필요
  2. 다양한 구현체 호환:

    • ArrayList, LinkedList 등 자유롭게 교체
    • 사용자 코드 안전
  3. 표준 설계 패턴:

    • Map, Set, Queue 도 같은 패턴
    • 자바 라이브러리의 황금률

→ "인터페이스로 명세, 추상클래스로 공통, 구체 클래스로 실현".


5️⃣ Spring/JPA 의 추상화

5.1 Spring 의 패턴

Spring 의 핵심 추상화 패턴:

interface JpaRepository<T, ID>     ← 외부 API
       ↑
       extends
       │
interface CrudRepository<T, ID>
       ↑
       extends
       │
interface Repository<T, ID>

// 구현
class SimpleJpaRepository<T, ID>    ← 추상클래스의 일종 (구체이지만)
    implements JpaRepositoryImplementation<T, ID> {
    
    // 모든 메서드 구현
}

5.2 Spring Data 의 인터페이스 의존

// 사용자 코드
@Repository
public interface ShipmentRepository extends JpaRepository<Shipment, Long> {
    
    // 메서드 시그니처만
    List<Shipment> findByStatus(ShipmentStatus status);
    Optional<Shipment> findByBlNo(String blNo);
}

// Spring 이 런타임에 자동 구현 생성
// → 사용자는 인터페이스만 정의
// → 구현은 Spring 의 SimpleJpaRepository + Proxy

특징:

  • 인터페이스만 정의: 강력한 추상화
  • Spring 의 자동 구현: 메서드 명만으로
  • 다중 분류 가능: 한 Entity 에 여러 Repository

5.3 Controller 의 추상화

// Spring MVC 의 다양한 방식

// 방식 1: 인터페이스 (옛 방식)
public interface Controller {
    ModelAndView handleRequest(HttpServletRequest request, 
                               HttpServletResponse response);
}

// 방식 2: 추상클래스
public abstract class AbstractController extends WebContentGenerator
        implements Controller {
    
    @Override
    public ModelAndView handleRequest(HttpServletRequest request, 
                                       HttpServletResponse response) {
        // 공통 로직
        return handleRequestInternal(request, response);
    }
    
    protected abstract ModelAndView handleRequestInternal(...);
}

// 방식 3: 어노테이션 (현대)
@Controller
public class ShipmentController {
    
    @GetMapping("/shipments")
    public List<Shipment> list() { ... }
    
    @PostMapping("/shipments")
    public Shipment create(@RequestBody Shipment s) { ... }
}

5.4 JPA Entity 의 추상화

// 공통 필드 추상화
@MappedSuperclass
public abstract class BaseEntity {
    
    @Id
    @GeneratedValue
    protected Long id;
    
    @CreatedDate
    protected LocalDateTime createdAt;
    
    @LastModifiedDate
    protected LocalDateTime updatedAt;
    
    // getter, setter
}

// 자식 엔티티들
@Entity
public class Shipment extends BaseEntity {
    private String blNo;
    private BigDecimal weight;
    // id, createdAt, updatedAt 자동 보유
}

@Entity
public class Cargo extends BaseEntity {
    private String type;
    private int quantity;
}

특징:

  • 모든 엔티티의 공통 필드를 추상클래스로
  • @MappedSuperclass — 테이블은 없지만 필드 상속

5.5 Service 패턴

// 인터페이스 정의
public interface ShipmentService {
    Shipment create(ShipmentCreateRequest request);
    Shipment findById(Long id);
    List<Shipment> findAll();
    Shipment update(Long id, ShipmentUpdateRequest request);
    void delete(Long id);
}

// 구현 클래스
@Service
public class ShipmentServiceImpl implements ShipmentService {
    
    private final ShipmentRepository repository;
    
    public ShipmentServiceImpl(ShipmentRepository repository) {
        this.repository = repository;
    }
    
    @Override
    public Shipment create(ShipmentCreateRequest request) { ... }
    
    @Override
    public Shipment findById(Long id) { ... }
    
    // ...
}

특징:

  • 인터페이스 → 외부 의존
  • 구현 클래스 → 실제 동작
  • 테스트 시 Mock 으로 쉽게 교체

5.6 Strategy 패턴 적용

// 인터페이스: 운임 계산 전략
public interface FareCalculator {
    BigDecimal calculate(Shipment shipment);
}

// 다양한 구현
@Component
public class SeaFareCalculator implements FareCalculator {
    @Override
    public BigDecimal calculate(Shipment shipment) {
        return shipment.getWeight().multiply(new BigDecimal("0.5"));
    }
}

@Component
public class AirFareCalculator implements FareCalculator {
    @Override
    public BigDecimal calculate(Shipment shipment) {
        return shipment.getWeight().multiply(new BigDecimal("3.0"));
    }
}

// Service 가 전략 선택
@Service
public class ShipmentService {
    
    private final Map<ShipmentMode, FareCalculator> calculators;
    
    public ShipmentService(List<FareCalculator> calculatorList) {
        // 자동 주입된 계산기들을 Map 으로
        this.calculators = ...;
    }
    
    public BigDecimal calculateFare(Shipment shipment) {
        FareCalculator calc = calculators.get(shipment.getMode());
        return calc.calculate(shipment);
    }
}

→ 인터페이스의 다중 구현체 + 런타임 선택.

5.7 Spring 의 추상화 종합

Spring 의 추상화 활용:

인터페이스 (외부):
  - Repository (JpaRepository 등)
  - Service (사용자 정의)
  - Strategy (계산기, 정책 등)
  - Event Listener
  - Functional Interface (람다)

추상클래스 (공통 구현):
  - BaseEntity (@MappedSuperclass)
  - AbstractController
  - SimpleJpaRepository
  - AbstractAuthenticationProcessingFilter

조합:
  - Repository (interface) + SimpleJpaRepository (구체)
  - JpaRepository<T, ID> + 자동 Proxy 구현

5.8 자기 점검 답변

Spring/JPA 에서 추상클래스와 인터페이스를 어떻게 활용하나?

:
1. 인터페이스 (외부 API):

  • Repository, Service
  • Spring 의 자동 구현 (Proxy)
  • 테스트 시 Mock 교체
  1. 추상클래스 (공통 구현):

    • @MappedSuperclass BaseEntity
    • AbstractController
    • 공통 필드/메서드 제공
  2. 조합 패턴:

    • 인터페이스 + 구현 클래스 (Service)
    • 인터페이스 + Spring 자동 구현 (Repository)
  3. Strategy 패턴:

    • 인터페이스의 다중 구현체
    • 런타임 선택

→ Spring 의 모든 곳에 추상화가 활용됨.


6️⃣ 사용자 정의 추상화 설계 가이드

6.1 설계 의사결정 흐름

설계 시 7단계 의사결정:

Step 1: 무엇을 추상화 하는가?
  - 분류 (Shape, Animal) → 추상클래스
  - 능력 (Drawable, Comparable) → 인터페이스

Step 2: 다중성 검토
  - 한 클래스가 여러 분류? → 인터페이스 추가
  - 단일 분류로 충분? → 추상클래스 가능

Step 3: 상태 검토
  - 공통 필드? → 추상클래스
  - 메서드만? → 인터페이스 + default

Step 4: 공통 구현 양 검토
  - 많음 (5+ 메서드) → 추상클래스
  - 적음 (간단한 default) → 인터페이스

Step 5: 진화 가능성
  - 추후 메서드 추가 자주 → 인터페이스 + default
  - 안정적 → 추상클래스

Step 6: 결합도
  - 강한 결합 OK → 추상클래스
  - 약한 결합 선호 → 인터페이스

Step 7: 결합 선택
  - 인터페이스만 → 단순 명세
  - 추상클래스만 → 강한 분류 + 구현
  - 둘 다 → Abstract + Interface 패턴 (권장)

6.2 설계 패턴 1 — Strategy

// 시나리오: 다양한 알고리즘
// → 인터페이스 + 구현체들

public interface CompressionAlgorithm {
    byte[] compress(byte[] data);
    byte[] decompress(byte[] data);
}

public class GzipCompression implements CompressionAlgorithm { ... }
public class ZipCompression implements CompressionAlgorithm { ... }
public class LzwCompression implements CompressionAlgorithm { ... }

public class FileCompressor {
    private CompressionAlgorithm algorithm;
    
    public FileCompressor(CompressionAlgorithm algorithm) {
        this.algorithm = algorithm;
    }
    
    public void compress(File file) {
        byte[] data = readFile(file);
        byte[] compressed = algorithm.compress(data);
        writeFile(file, compressed);
    }
}

→ 인터페이스 + 다중 구현.

6.3 설계 패턴 2 — Template Method

// 시나리오: 알고리즘 골격 + 변하는 단계
// → 추상클래스 + 자식들

public abstract class DataProcessor {
    
    // Template Method
    public final void process(InputStream input) {
        Data data = read(input);
        validate(data);
        Data transformed = transform(data);
        save(transformed);
        notify(transformed);
    }
    
    // 공통 구현
    private Data read(InputStream in) { ... }
    private void save(Data d) { ... }
    private void notify(Data d) { ... }
    
    // 자식 구현
    protected abstract void validate(Data d);
    protected abstract Data transform(Data d);
}

public class CsvDataProcessor extends DataProcessor {
    @Override
    protected void validate(Data d) { ... }
    
    @Override
    protected Data transform(Data d) { ... }
}

public class JsonDataProcessor extends DataProcessor { ... }

→ 추상클래스 + 자식들.

6.4 설계 패턴 3 — 결합 (Abstract + Interface)

// 시나리오: 분류 + 능력 + 공통 구현
// → 인터페이스 + 추상클래스 + 구체

// 1. 인터페이스 (외부 명세)
public interface ShipmentRepository {
    Shipment save(Shipment s);
    Optional<Shipment> findById(Long id);
    List<Shipment> findAll();
    void delete(Shipment s);
}

// 2. 추상클래스 (공통 구현)
public abstract class AbstractShipmentRepository implements ShipmentRepository {
    
    protected final ShipmentValidator validator;
    protected final ShipmentEventPublisher publisher;
    
    protected AbstractShipmentRepository(
            ShipmentValidator validator,
            ShipmentEventPublisher publisher) {
        this.validator = validator;
        this.publisher = publisher;
    }
    
    @Override
    public Shipment save(Shipment s) {
        validator.validate(s);
        Shipment saved = doSave(s);
        publisher.publishCreated(saved);
        return saved;
    }
    
    @Override
    public void delete(Shipment s) {
        Shipment found = findById(s.getId()).orElseThrow();
        doDelete(found);
        publisher.publishDeleted(found);
    }
    
    // 자식이 구현할 핵심
    protected abstract Shipment doSave(Shipment s);
    protected abstract void doDelete(Shipment s);
}

// 3. 구체 클래스들
public class JpaShipmentRepository extends AbstractShipmentRepository {
    private final EntityManager em;
    
    @Override
    protected Shipment doSave(Shipment s) {
        return em.merge(s);
    }
    
    @Override
    protected void doDelete(Shipment s) {
        em.remove(s);
    }
}

public class InMemoryShipmentRepository extends AbstractShipmentRepository {
    private final Map<Long, Shipment> store = new HashMap<>();
    
    @Override
    protected Shipment doSave(Shipment s) {
        store.put(s.getId(), s);
        return s;
    }
    
    @Override
    protected void doDelete(Shipment s) {
        store.remove(s.getId());
    }
}

→ 외부엔 인터페이스, 내부엔 추상클래스, 실제는 구체 클래스.

6.5 결정 기준 요약

시나리오별 권장:

다중 분류 + 능력만:
  → 인터페이스 (다중 구현)
  예: Comparable, Iterable

단일 분류 + 공통 상태:
  → 추상클래스
  예: Number, InputStream

분류 + 능력 + 공통 구현:
  → 인터페이스 + 추상클래스 결합
  예: AbstractList implements List
  
단순 능력 + 기본 구현:
  → 인터페이스 + default
  예: Comparator.reversed()

복잡한 알고리즘 골격:
  → 추상클래스 + Template Method
  예: AbstractHandler

전략 패턴:
  → 인터페이스 + 다양한 구현
  예: CompressionAlgorithm

상수 모음:
  → final class + private 생성자 + static
  ❌ 인터페이스 X (안티패턴)

6.6 설계 시 흔한 실수

실수 1: 모든 것을 인터페이스로
  - 작은 프로젝트에서 과한 추상화
  - 단순한 코드를 복잡하게

실수 2: 모든 것을 추상클래스로
  - 다중 분류 못 함
  - 유연성 ↓

실수 3: LSP 위반
  - 부모를 자식이 부정
  - 예: Bird.fly() 가 Penguin 에서 부정

실수 4: 깊은 상속 계층
  - 5단계 이상 상속
  - 추적 어려움
  - 유지보수 ↓

실수 5: 상수 인터페이스
  - implements 만으로 상수 노출
  - 캡슐화 위반

실수 6: 익명 클래스 남용
  - 복잡한 익명 클래스
  - 가독성 ↓
  - 별도 클래스로 분리 권장

6.7 모범 설계 원칙

1. 인터페이스를 먼저 설계
   - 사용자 관점에서 시작
   - 명세 명확화
   - 구현은 나중에

2. 작은 인터페이스 선호 (ISP)
   - Interface Segregation Principle
   - 큰 인터페이스 분리
   - 각 클라이언트가 필요한 것만

3. 합성을 상속보다 우선 (CIP)
   - "is-a" 관계가 명확할 때만 상속
   - 그 외엔 컴포지션

4. 의존성 역전 (DIP)
   - 추상에 의존, 구체에 의존 X
   - 인터페이스 의존

5. 단일 책임 (SRP)
   - 한 클래스/인터페이스 = 한 책임
   - 큰 인터페이스 = 책임 분산

6.8 자기 점검 답변

사용자 정의 추상화 설계 시 가장 중요한 첫 질문은?

:
1. "무엇을 추상화하는가?":

  • 분류 (is-a) → 추상클래스
  • 능력 (can-do) → 인터페이스
  1. "누가 사용하는가?":

    • 외부 사용자 → 인터페이스
    • 자식 클래스 → 추상클래스
  2. "진화 가능성?":

    • 자주 메서드 추가 → 인터페이스 + default
    • 안정적 → 추상클래스
  3. "다중 분류?":

    • 필요 → 인터페이스
    • 불필요 → 추상클래스 가능

→ 결국 "인터페이스 + 추상클래스 결합" 이 가장 권장.


7️⃣ Sealed 인터페이스/클래스 (Java 17+)

7.1 Sealed 의 등장 배경

기존의 문제:

1. 인터페이스/클래스 확장의 자유 너무 많음
   - 누가 어떻게 구현할지 모름
   - 모든 가능성 고려해야

2. Pattern Matching 의 완전성
   - switch 에서 모든 케이스 검증
   - 새 자식 추가 시 누락 가능

해결:
  sealed = "내가 허용한 자식만 확장 가능"

7.2 Sealed 클래스 문법

// Java 17+
public sealed class Shape 
    permits Circle, Rectangle, Triangle {
    // ...
}

// 허용된 자식들
public final class Circle extends Shape {
    private double radius;
}

public final class Rectangle extends Shape {
    private double width;
    private double height;
}

public final class Triangle extends Shape {
    private double base;
    private double height;
}

// ❌ Square 추가 시도 — 컴파일 에러
// public class Square extends Shape { }
// → permits 에 없음

7.3 자식 클래스의 3가지 옵션

public sealed class Shape permits Circle, Polygon { }

// 1. final — 더 이상 확장 안 됨
public final class Circle extends Shape { }

// 2. sealed — 자기도 sealed (또 다른 permits 지정)
public sealed class Polygon extends Shape 
    permits Triangle, Rectangle { }

// 3. non-sealed — 자유 확장 허용
public non-sealed class Triangle extends Polygon { }

// 이 후 Triangle 은 자유 확장 가능
public class IsoscelesTriangle extends Triangle { }   // OK

7.4 Sealed 인터페이스

public sealed interface Vehicle 
    permits Car, Truck, Motorcycle {
    
    int wheels();
}

public final class Car implements Vehicle {
    @Override public int wheels() { return 4; }
}

public final class Truck implements Vehicle {
    @Override public int wheels() { return 6; }
}

public final class Motorcycle implements Vehicle {
    @Override public int wheels() { return 2; }
}

7.5 Pattern Matching 과 결합

public sealed interface Shape 
    permits Circle, Rectangle, Triangle { }

// Java 21+ Pattern Matching for switch
public double area(Shape shape) {
    return switch (shape) {
        case Circle c -> Math.PI * c.radius() * c.radius();
        case Rectangle r -> r.width() * r.height();
        case Triangle t -> 0.5 * t.base() * t.height();
        // default 불필요 — sealed 라 완전
    };
}

특징:

  • 모든 자식 케이스 강제
  • 새 자식 추가 시 switch 도 추가 (컴파일 에러)
  • 완전성 보장

7.6 Sealed 의 활용

// 비즈니스 도메인 모델링
public sealed interface ShipmentStatus
    permits Draft, Active, Delivered, Cancelled { }

public final record Draft() implements ShipmentStatus { }
public final record Active(LocalDateTime startedAt) implements ShipmentStatus { }
public final record Delivered(LocalDateTime arrivedAt) implements ShipmentStatus { }
public final record Cancelled(String reason) implements ShipmentStatus { }

// 사용
public String describe(ShipmentStatus status) {
    return switch (status) {
        case Draft d -> "Draft shipment";
        case Active a -> "Shipping since " + a.startedAt();
        case Delivered d -> "Delivered at " + d.arrivedAt();
        case Cancelled c -> "Cancelled: " + c.reason();
    };
}

→ ADT (Algebraic Data Type) 흉내.

7.7 Sealed vs Abstract

Sealed:
  - 확장 가능 (단, permits 자식만)
  - Pattern Matching 완전성
  - 명시적 자식 목록

Abstract:
  - 무한 확장 (final 아니면)
  - Pattern Matching 완전성 X
  - 자식 누구든 가능

선택:
  - 도메인 모델 (제한된 자식): sealed
  - 라이브러리 (확장 가능): abstract

7.8 제약과 한계

Sealed 의 제약:

1. permits 명시 필수
   - 자식이 같은 패키지 또는 같은 모듈
   - 동적 확장 X

2. 자식이 final / sealed / non-sealed 중 하나
   - 자유로운 확장 못 함
   - 의도 명시

3. permits 의 자식들이 컴파일 시 존재
   - 별도 모듈로 분리 어려움

7.9 자기 점검 답변

Sealed 인터페이스가 일반 인터페이스와 다른 점은?

:
1. 확장 제한:

  • 일반: 누구나 implements
  • Sealed: permits 의 자식만
  1. Pattern Matching 완전성:

    • 일반: switch 에 default 필요
    • Sealed: 모든 케이스 명시 가능
  2. 자식의 의무:

    • final, sealed, non-sealed 중 선택
    • 의도 명시
  3. 활용:

    • 도메인 모델 (제한된 상태)
    • ADT (Algebraic Data Type)
    • 변하지 않는 분류

→ Java 17+ 의 새 도구로 표현력 ↑.


8️⃣ Phase 4 졸업 시험 — 30 문항

8.1 졸업 시험

다음 30 문항에 즉답할 수 있다면 Phase 4 마스터.

추상클래스 (10 문항)

Q1. abstract 키워드의 두 가지 사용은?
A1. 클래스 (인스턴스화 불가) + 메서드 (본체 없음)

Q2. 추상클래스가 인스턴스화 되지 않는 이유는?
A2. 일부 메서드가 미구현, 자식이 완성해야

Q3. 추상클래스의 5가지 멤버는?
A3. 필드, 생성자, 구현 메서드, 추상 메서드, static 메서드

Q4. 추상 메서드의 4가지 제약은?
A4. 본체 X, private X, final X, static X

Q5. 추상클래스에 생성자가 있는 이유는?
A5. 자식이 super() 로 호출, 부모 필드 초기화

Q6. abstract + final 이 에러인 이유?
A6. 모순 — 확장 vs 닫음

Q7. Template Method 패턴이란?
A7. 알고리즘 골격은 부모, 변하는 단계는 자식

Q8. 단일 상속의 이유?
A8. Diamond Problem 회피

Q9. 추상클래스 다중 상속 가능?
A9. 불가 (자바는 클래스 단일 상속만)

Q10. 추상클래스에 main 메서드 가능?
A10. 가능 (static 이라 무관)

인터페이스 (10 문항)

Q11. 인터페이스 메서드의 자동 수식자는?
A11. public abstract (Java 7 까지)

Q12. 인터페이스 필드의 자동 수식자는?
A12. public static final (상수)

Q13. 다중 구현이 가능한 이유는?
A13. 구현 없으니 메서드 해결 모호함 X

Q14. 인터페이스에 생성자가 없는 이유는?
A14. 인스턴스화 불가 + 상태 없음 + 다중 구현 모호함

Q15. 마커 인터페이스의 예 3가지?
A15. Serializable, Cloneable, RandomAccess

Q16. 함수형 인터페이스란?
A16. 추상 메서드 정확히 1개 (SAM)

Q17. @FunctionalInterface 어노테이션은?
A17. 검증 어노테이션 (추상 2개 이상이면 에러)

Q18. 인터페이스 vs 추상클래스 가장 큰 차이?
A18. 다중 vs 단일 + can-do vs is-a

Q19. 인터페이스에 인스턴스 필드 가능?
A19. 불가 (모든 필드 자동 static final)

Q20. 인터페이스 final 가능?
A20. 불가 (본질이 확장)

default/static/private (5 문항)

Q21. default 메서드의 등장 이유?
A21. 하위 호환성 + Stream API 도입

Q22. Diamond Problem 의 부활?
A22. Java 8 default 메서드로

Q23. Diamond 해결 3가지 규칙?
A23. 클래스 우선, 구체적 우선, 명시적 해결

Q24. 인터페이스 static 메서드 호출?
A24. 인터페이스명.메서드() (구현체 X)

Q25. Java 9 private 메서드 이유?
A25. default 간 코드 공유, 외부 노출 회피

선택 기준 + 종합 (5 문항)

Q26. 5가지 결정 기준?
A26. 관계 유형, 다중성, 상태, 공통 구현, 확장 의도

Q27. AbstractList + List 패턴?
A27. 외부엔 인터페이스, 내부 공통은 추상클래스, 실제는 구체

Q28. is-a vs can-do?
A28. 분류 vs 능력, 추상클래스 vs 인터페이스

Q29. Sealed 인터페이스의 의도?
A29. 확장 제한 + Pattern Matching 완전성

Q30. 상수만 모은 인터페이스 안티패턴 회피?
A30. final class + private 생성자 + public static final

8.2 채점

30 / 30 → Phase 4 마스터
25-29   → 거의 마스터, 일부 복습
20-24   → 핵심 다시 학습
< 20    → Unit 4.1 ~ 4.4 재학습

9️⃣ Phase 4 완주 + Phase 5 진입

9.1 Phase 4 학습 종합

Phase 4 — 추상화의 두 도구

Unit 4.1 — 추상클래스의 특징
  - abstract 키워드
  - 부분 구현
  - 7가지 제약
  - Template Method 패턴
  - 자바 표준 활용

Unit 4.2 — 인터페이스의 특징
  - interface 키워드
  - 자동 수식자
  - 다중 구현
  - 마커 인터페이스
  - 함수형 인터페이스

Unit 4.3 — Java 8 default & static 메서드
  - 등장 배경 (하위 호환성)
  - Diamond Problem 부활과 해결
  - 인터페이스 static 메서드
  - private 메서드 (Java 9+)
  - 자바 표준 활용

Unit 4.4 — 추상클래스 vs 인터페이스 선택 기준
  - 5가지 결정 기준
  - 자바 표준 분석
  - AbstractList + List 패턴
  - Spring/JPA 의 추상화
  - Sealed 인터페이스

9.2 Phase 4 마스터 후 가능한 일

1. 자바 추상화 도구 자유롭게 선택
   - 시나리오 분석
   - 5가지 기준 적용
   - 적절한 도구 결정

2. 자바 표준 라이브러리 깊이 이해
   - AbstractList + List
   - InputStream + Closeable
   - JpaRepository + SimpleJpaRepository

3. 좋은 추상화 설계
   - 인터페이스 우선
   - 추상클래스 결합
   - Strategy + Template Method

4. 면접 자신감
   - 추상클래스 vs 인터페이스
   - default 메서드의 의미
   - Diamond Problem 해결

5. Java 17+ 새 기능 활용
   - Sealed 인터페이스/클래스
   - Pattern Matching
   - Records 와의 결합

9.3 Phase 5 진입 — 제네릭과 와일드카드

다음 Phase 5 — 제네릭과 와일드카드

Unit 5.1 — 제네릭의 등장과 raw type
  → Java 5 이전의 문제 + 제네릭 도입

Unit 5.2 — 타입 매개변수와 타입 인자
  → T, E, K, V, R 등의 의미

Unit 5.3 — 와일드카드 ?
  → unbounded, upper-bounded, lower-bounded

Unit 5.4 — 제네릭 메서드 + 제네릭 클래스
  → 메서드 매개변수의 제네릭

Unit 5.5 — PECS 원칙 (마스터 깊이)
  → Producer Extends Consumer Super
  → Comparable, Comparator 활용

9.4 Phase 4 → Phase 5 의 연결

Phase 4: 추상화 (인터페이스, 추상클래스)
   ↓
Phase 5: 제네릭 (타입 매개변수)

연결:
  - 인터페이스의 제네릭: Comparable<T>, Iterable<E>
  - 컬렉션의 제네릭: List<E>, Map<K, V>
  - 함수형 인터페이스: Function<T, R>, Predicate<T>
  - 와일드카드: List<? extends Number>

9.5 3주차 누적 진행

✅ Phase 1 — Pass by Value (3 Unit)
✅ Phase 2 — 컬렉션 프레임워크 (6 Unit)
✅ Phase 3 — 해시의 원리 (4 Unit)
✅ Phase 4 — 추상화의 두 도구 (4 Unit)  ← 완주
🚀 Phase 5 — 제네릭과 와일드카드 (다음)
⏭ Phase 6 — 객체 비교
⏭ Phase 7 — I/O 시스템 큰 그림
⏭ Phase 8 — Stream 실전
⏭ Phase 9 — I/O 강화
⏭ Phase 10 — 함수형 프로그래밍

총: 17/43 Unit 작성 (Phase 4 완주, 약 40%)

🎯 핵심 요약 — 3줄 정리

1. 5가지 결정 기준

  • 관계 유형 (is-a vs can-do)
  • 다중성 (단일 vs 다중)
  • 상태 (필드 X vs O)
  • 공통 구현 양 (단순 vs 복잡)
  • 확장 의도 (능력 vs 골격)

2. 표준 패턴 — 인터페이스 + 추상클래스 결합

  • 인터페이스: 외부 API
  • 추상클래스: 공통 구현
  • 구체 클래스: 실제 사용
  • 예: AbstractList implements List ← ArrayList

3. 도구의 진화

  • Java 1.0: 추상클래스 + 인터페이스 (순수 명세)
  • Java 8: default/static (인터페이스 진화)
  • Java 9: private (코드 공유)
  • Java 17+: sealed (확장 제한)

🏆 Phase 4 완주 — 추상화 마스터 달성

🚀 Phase 4 — 추상화의 두 도구
  ✅ Unit 4.1 추상클래스의 특징
  ✅ Unit 4.2 인터페이스의 특징
  ✅ Unit 4.3 Java 8 default & static 메서드
  ✅ Unit 4.4 추상클래스 vs 인터페이스 선택 기준 ← 여기, Phase 4 완주

→ 자바 추상화의 두 도구 정복
→ 5가지 결정 기준으로 자유로운 선택
→ 표준 라이브러리/Spring/JPA 깊이 이해
→ 사용자 정의 추상화 설계 가능

📚 다음으로...

Phase 5 — 제네릭과 와일드카드

다음 Phase 는 자바의 타입 시스템 정복.

Phase 5 — 제네릭과 와일드카드

Unit 5.1 — 제네릭의 등장과 raw type
  → "List 안에 String 보장은 어떻게?"
  
Unit 5.2 — 타입 매개변수와 타입 인자
  → T, E, K, V, R 의 의미와 사용
  
Unit 5.3 — 와일드카드 ? extends, ? super
  → 공변성, 반공변성
  
Unit 5.4 — 제네릭 메서드 + 제네릭 클래스
  → Collections.sort 의 시그니처
  
Unit 5.5 — PECS 원칙 (★ 마스터 프롬프트 깊이)
  → Producer Extends Consumer Super
  → Comparable, Comparator 의 정밀

Phase 4 와의 연결:

  • 인터페이스의 제네릭: List, Comparable
  • 함수형 인터페이스: Function<T, R>
  • 추상클래스의 제네릭: AbstractList

🏆 Phase 4 완주 — 추상화 마스터 달성

profile
Software Developer

0개의 댓글