자바를 다시 공부하기 시작하면서, 추상 클래스와 인터페이스의 용도에 어떤 차이가 있는지 잘 모르겠다는 생각이 들었다. 그래서 이번에는 이 둘의 차이점은 무엇이 있는지 한번 알아보려고 한다.

공통점

추상 클래스와 인터페이스는 둘 다 함수에 대한 선언만 하고, 구현하지 않는 메소드가 있다는 공통점이 있다. 완전히 구현된 객체가 아니기 때문에 구현 클래스가 있어야 인스턴스로 생성이 가능하다.

언뜻 보기에 추상 클래스와 인터페이스 모두 추상 메소드 구현을 강제한다는 점에서 비슷하다. implements, extends를 통해 사용한 다는 차이만 있는 것 같아보이기도 한다. 하지만 추상 메소드는 한개만 extends 할 수 있고, 인터페이스는 여러개implements할 수 있다.

차이점

상속할 수 있는 개수의 차이도 있지만, 상속 했을 때 구현되는 메서드나 변수의 접근 제한자에서도 차이가 발생한다.

인터페이스에서 상수의 기본 형태는 public static final이고, 메소드는 public abstract형태이다. 따라서 이를 implements한 클래스의 메소드도 모두 public 형태로 구현되어야 한다.

그러나 추상 클래스는 다양한 접근 제한자(public, protected, private)를 가질 수 있다. 따라서 추상 클래스에서는 상속한 이후에도 그보다 하위의 접근 제한자를 가진 메서드와 변수를 구현할 수 있게 된다.

JDK7까지는 추상 클래스에서는 추상 메소드와 일반 메소드를 모두 구현할 수 있었는데 인터페이스는 추상 메소드 작성만 가능해 용도의 정확한 차이까진 모르더라도, 추상 클래스는 구현된 메서드도 있는 클래스 정도로 기억할 수 있었다. 그런데 JDK8이 되면서, 인터페이스에 default 메소드가 추가되었고, 일반 메소드도 구현이 가능해졌다.

별차이 없는 것 같은데? 언제 추상 클래스를 쓰고, 인터페이스를 쓴다는거야?

추상 클래스

  • public 이외의 접근 제어자가 필요한 경우
  • non-static, non-final 필드가 필요한 경우
  • 관련성이 높은 클래스 간 코드를 공유하려는 경우

인터페이스

  • 다중 사용을 허용하고 싶은 경우
  • 관련성이 낮은 클래스 간 메소드를 공유하려는 경우

이런 경우에 추상 클래스와 인터페이스를 사용하는 경우를 나눈다고 하는데, 관련성이 높고 관련성이 낮은 클래스라는 말이 잘 와닿지 않았다. 그래서 Java에서는 어떨 때 추상 클래스를 사용해 구현했고, 어떨 때 인터페이스를 사용해서 Class들을 구현해놨는지 한번 살펴보기로 했다.

가장 와닿았던 클래스 객체는 Collection과 관련된 객체들이었다.

추상 클래스의 대표적 예는 Collections Framwork의 AbstractCollection이다.
AbstractCollectionAbstractList, AbstractMap, AbstractSet, AbstractQueue 등의 자식 추상 클래스를 가지고, add, clear, isEmpty, iterator 등의 메소드를 공유하고 있다. Collection이라는 관련성이 높은 클래스끼리 비슷한 동작을 공유한다.

인터페이스의 대표적 예는 Iterable이 있다. 이 인터페이스를 AbstractCollection, ArrayDeque, SQLException, SQLWarning, Stack, Vector 등 많은 클래스들이 implements하고 있다. 하지만 이 클래스들은 순차적으로 데이터가 필요하다는 특징 외에는 관련이 없는 클래스로, 관련성이 낮은 클래스들이다.

이러한 특징 때문에 공통적인 속성들을 모아놓은 클래스인 추상 클래스는 그 구현체가 크면 클수록 좋고, 세부적인 속성들을 부여하는 클래스인 인터페이스는 그 구현체가 작으면 작을수록 활용도가 높아져 좋다고 한다.


예시를 통해 확인해보니 차이가 조금 보인다. 예시를 들려고 API를 한번 살펴보면서, 새삼 대단하다는 생각을 했다. 물론 한번에 작성된 코드들은 아니지만 설계의 중요성도 느낄 수 있었다.

또 다른 자바의 API들을 보고싶다면, 오라클의 API 문서를 참고하자.

자바의 세계는 알면 알수록 신기하다 :)