상속이 캡슐화를 위반하는 이유

2025. 11. 4. 23:00·Object-oriented programming

객체지향 공부를 하던 중 객체지향의 특징인 상속이 캡슐화를 위반한다는 글을 보게 되었다.

결론부터 말하자면 상속이 항상 캡슐화를 위반하는 것은 아니다.

그럼 어떤 상속이 캡슐화를 위반할까?

한번 알아보자.

 

 

상속

객체 지향에서의 상속은 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)받고 거기에 더해 필요한 특성을 추가하는 것.

즉, 확장해서 사용할 수 있다는 뜻이다.

 

캡슐화

객체의 데이터와 동작을 하나로 묶어 실제 구현 내용을 외부에 공개하지 않는 것

즉, 객체가 내부적으로 기능을 어떻게 구현하는지를 감추는 것이다.

 

상속과 캡슐화는 객체지향의 4대 특성이다.

하지만, 아이러니하게도 상속은 캡슐화를 위반한다.

 

예제를 통해 알아보자.

// Broken - Inappropriate use of inheritance!
public class InstrumentedHashSet<E> extends HashSet<E> {
    // The number of attempted element insertions
    private int addCount = 0;
    
    public InstrumentedHashSet() {
    }
    
    public InstrumentedHashSet(int initCap, float loadFactor) {
        super(initCap, loadFactor);
    }
    
    @Override
    public boolean add(E e) {
        addCount++;
        return super.add(e);
    }
    
    @Override
    public boolean addAll(Collection<? extends E> c) {
        addCount += c.size();
        return super.addAll(c);
    }
    
    public int getAddCount() {
        return addCount;
    }
}

`InstrumentedHashSet`는 `HashSet`에 추가되는 요소 개수를 계산하는 클래스이다.

요소가 추가될 때마다 계산하기 위해 `add()`와 `addAll()`을 오버라이딩하여 addCount에 요소 개수를 카운팅한다.

 

public class ExtendsTest {
    public static void main(String[] args) {
        InstrumentedHashSet<String> set = new InstrumentedHashSet<>();
        set.addAll(List.of("Snap", "Crackle", "Pop"));

        System.out.println("addCount: " + set.getAddCount());
    }
}

"Snap", "Crackle", "Pop" 3개의 요소를 추가했다.

그럼 `InstrumentedHashSet`에서 오버라이딩한 `addAll()`이 호출될 것이고, `addCount`의 기댓값은 3일 것이다.

 

하지만, `addCount`의 값은 6이 된다.

분명 3개의 요소를 추가했는데 `addCount`가 6이 된다니? 왜일까?

 

`addAll()`의 코드를 살펴보면 이유를 알 수 있다.
@Override
public boolean addAll(Collection<? extends E> c) {
    addCount += c.size();
    return super.addAll(c);
}

`addCount += c.size()`

addCount = 3

 

`super.addAll(c)`

상위 클래스(`HashSet`)의 `addAll()`를 호출한다.

 

public boolean addAll(Collection<? extends E> c) {
    boolean modified = false;
    for (E e : c)
        if (add(e))
            modified = true;
    return modified;
}

위 코드는 상위 클래스 `HashSet`의 `addAll()`이다. 살펴보면 내부 구현에서는 `add()`를 호출하는 것을 볼 수 있다.

현재 인스턴스는 `HashSet`이 아닌 `InstrumentedHashSet`이기 때문에 `add()`는 오버라이딩된 `InstrumentedHashSet`의 `add()`가 호출된다.

 

요소 3개만큼 반복문을 진행하기 때문에 `add()`가 3번 더 호출되어 `addCount`는 6이 되는 것이다.

 

 

 

문제점

  1. `InstrumentedHashSet`는 `HashSet`의 `addAll()`의 내부 구현을 모르는 상태로 오버라이딩했지만 메서드가 오작동함.
  2. 코드를 수정하려면 `HashSet`의 `addAll()`의 내부 구현을 알아야 함.

여기서 문제점은 이러한 문제를 해결하려면 부모 클래스의 구현 내용을 알아야 하고, 이를 해결에 반영하면 그 내용을 노출할 가능성이 발생한다.

 

부모 클래스의 구현 내용을 노출하게 된다면 캡슐화 위반이 되는 것이다.

 

 

 

그럼 오버라이딩때문에 캡슐화를 위반한 것일까?

`addAll()`을 오버라이딩함으로써 캡슐화를 위반하게 됐다.

하지만, 오버라이딩으로 인해 무조건 캡슐화를 위반하는 것은 아니다.

 

class Animal {
    public void breathe() {
        System.out.println("호흡하다");
    }
}

class Whale extends Animal {
    public void breathe() {
        System.out.println("폐로 호흡하다");
    }
}

`breathe()`는 하위 클래스에서 오버라이딩했지만, 상위 클래스와 하위 클래스에서 각각 다르게 구현됐다.

이 경우 오버라이딩으로 인해 상위 클래스의 내부 동작이 노출되거나 변경되지 않는다.

 

즉, 하위 클래스에서 오버라이딩을 해도, 상위 클래스와 하위 클래스 모두 해당 메서드를 독립적으로 구현해서 상위 클래스의 내부 동작이 노출되거나 변경되지 않는다면 캡슐화를 위반하지 않는다.

 

 

 

결론

상속 관계에서 상위 클래스의 내부 동작이 노출 또는 변경되지 않으면 캡슐화를 위반하지 않는다.

하지만, 오버라이딩으로 인해 상위 클래스의 내부 동작이 노출되거나 변경될 가능성이 발생한다.

 

상속이 캡슐화는 위반하는 이유는 캡슐화를 위반할 가능성이 있는 오버라이딩을 상속이 제공해주기 때문이다.

 

그래서 제임스 고슬링도 상속보다는 구현을 권장한다고 한다!

 

 

 

참고

https://inpa.tistory.com/entry/OOP-💠-객체-지향의-상속-문제점과-합성Composition-이해하기

https://unluckyjung.github.io/oop/2021/03/17/Inheritance-and-Encapsulation/

https://dev-rootable.tistory.com/156

https://blogs.oracle.com/javamagazine/post/java-inheritance-composition

Joshua Bloch의 글에 있는 예제를 인용했는데 글이 삭제되었다…

'Object-oriented programming' 카테고리의 다른 글

객체 지향 프로그래밍 (Object-Oriented Programming, OOP)  (0) 2024.08.04
'Object-oriented programming' 카테고리의 다른 글
  • 객체 지향 프로그래밍 (Object-Oriented Programming, OOP)
말차쟁이
말차쟁이
  • 말차쟁이
    Hello world
    말차쟁이
  • 전체
    오늘
    어제
    • 분류 전체보기 (12)
      • SOSO (0)
      • Java (4)
      • Spring (0)
      • Computer Science (1)
      • Object-oriented programming (2)
      • DataBase (0)
      • Git (0)
      • Algorithm (2)
      • Degisn Pattern (0)
      • Network (0)
      • Trouble Shooting (2)
      • 기타 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    백준
    자바
    알고리즘
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
말차쟁이
상속이 캡슐화를 위반하는 이유
상단으로

티스토리툴바