자바(Java)에서 추상화(abstraction)는 추상 클래스(Abstract Class)와 인터페이스(Interface)를 통해 구현됩니다. 두 방식 모두 하위 클래스 또는 구현 클래스가 반드시 오버라이딩해야 하는 추상 메서드를 포함할 수 있다는 공통점이 있습니다. 하지만 문법적 제약과 설계 목적에서 중요한 차이가 존재합니다. 아래 표에서 추상 클래스와 인터페이스의 주요 차이점을 항목별로 비교해 보겠습니다.
| 번호 | 구분 기준 | 추상 클래스 (Abstract Class) | 인터페이스 (Interface) |
|---|---|---|---|
| 1 | 지원 메서드 | 추상 메서드와 일반(구체) 메서드를 모두 가질 수 있습니다. | 기본적으로 추상 메서드만 가질 수 있으며, Java 8부터는 default 메서드와 static 메서드도 허용됩니다. |
| 2 | 다중 상속 | 다중 상속을 지원하지 않습니다. | 다중 상속(다중 구현)을 지원합니다. |
| 3 | 지원 변수 | final, non-final, static, non-static 변수를 모두 사용할 수 있습니다. | static과 final 변수만 선언할 수 있습니다. |
| 4 | 구현 관계 | 인터페이스를 구현(implement)할 수 있습니다. | 다른 인터페이스를 구현할 수는 없으며, 확장(extend)만 가능합니다. |
| 5 | 선언 키워드 | abstract 키워드로 선언합니다. | interface 키워드로 선언합니다. |
| 6 | 상속 범위 | extends 키워드로 다른 클래스를 상속받으면서 동시에 인터페이스를 구현할 수 있습니다. | 인터페이스만 상속(확장)할 수 있습니다. |
| 7 | 상속 방식 | extends 키워드를 사용해 상속받습니다. | implements 키워드를 사용해서만 구현할 수 있습니다. |
| 8 | 접근 제어 | private, public 등 모든 종류의 멤버와 접근 제어자를 사용할 수 있습니다. | public 멤버만 허용됩니다. |
추상 클래스 vs 인터페이스 예제 코드
아래 예제는 하나의 프로그램에서 인터페이스(Animal)와 추상 클래스(Cat)를 각각 구현·상속하여 사용하는 방법을 보여줍니다.
JavaTester.java
public class JavaTester {
public static void main(String args[]) {
Animal tiger = new Tiger();
tiger.eat();
Cat lion = new Lion();
lion.eat();
}
}
interface Animal {
public void eat();
}
class Tiger implements Animal {
public void eat(){
System.out.println("Tiger eats");
}
}
abstract class Cat {
abstract public void eat();
}
class Lion extends Cat{
public void eat(){
System.out.println("Lion eats");
}
}실행 결과
Tiger eats Lion eats
어떤 것을 선택해야 할까?
두 개념의 선택 기준은 다음과 같이 정리할 수 있습니다.
추상 클래스는 관련 있는 클래스들 간에 공통된 상태(필드)와 부분적으로 구현된 기능을 공유하고 싶을 때 적합합니다. 예를 들어 여러 동물 클래스가 공통으로 사용하는 필드나 일부 완성된 메서드가 필요하다면 추상 클래스가 좋은 선택입니다.
인터페이스는 서로 관련 없는 클래스들에게 특정 기능 수행 능력을 부여하는 계약(contract) 역할이 필요할 때 적합합니다. 또한 Java 8 이후에는 default 메서드 덕분에 인터페이스에서도 기본 구현을 제공할 수 있어 활용 범위가 더욱 넓어졌습니다.
일반적으로 "~이다(is-a)" 관계의 공통 기반을 만들 때는 추상 클래스를, "~할 수 있다(can-do)" 같은 기능 명세를 정의할 때는 인터페이스를 사용하는 것이 자바 설계의 모범 사례로 꼽힙니다.