추상클래스와 인터페이스 차이

서버란·2024년 8월 30일

자바 궁금증

목록 보기
3/35

추상 클래스와 인터페이스의 차이점 및 사용법 정리

자바에서 추상 클래스와 인터페이스는 객체 지향 프로그래밍의 핵심 요소로, 다른 클래스나 객체가 상속 및 구현을 통해 공통된 행동을 정의하고 구조를 만들 수 있도록 돕는 역할을 합니다. 하지만 이 두 개념은 설계와 사용 목적에서 큰 차이점을 가지고 있습니다. 이번 글에서는 추상 클래스와 인터페이스의 차이점, 특징, 그리고 각각의 사용 목적에 대해 공부하기 쉽게 정리해 보았습니다.

1. 추상 클래스란?

추상 클래스는 미완성 설계도로 비유할 수 있으며, 상속을 통해 자손 클래스에서 구현을 완료하도록 유도하는 클래스입니다.

주요 특징

  • 객체 생성 불가: 추상 클래스는 직접 객체를 생성할 수 없습니다. 상속을 받아 완전한 클래스가 되어야 객체 생성이 가능합니다.
  • 추상 메서드 선언: 하나 이상의 추상 메서드(구현되지 않은 메서드)를 가질 수 있습니다. 이 추상 메서드는 자손 클래스에서 반드시 구현해야 합니다.
  • 일반 메서드도 포함: 추상 클래스는 추상 메서드뿐만 아니라 일반 메서드도 포함할 수 있습니다.
  • 상속을 위한 클래스: 추상 클래스는 상속을 통해 자손 클래스에서 추가 구현을 제공할 수 있도록 설계된 클래스입니다.
    예시
abstract class Animal {
    abstract void sound();  // 추상 메서드
    void sleep() {          // 일반 메서드
        System.out.println("The animal is sleeping");
    }
}

class Dog extends Animal {
    @Override
    void sound() {
        System.out.println("Bark");
    }
}

Dog 클래스는 Animal 클래스를 상속받아 추상 메서드인 sound()를 구현해야 합니다.

2. 인터페이스란?

인터페이스는 기본 설계도 역할을 하며, 클래스가 특정 기능을 구현할 수 있도록 가이드라인을 제공합니다. 인터페이스는 다중 상속을 가능하게 하고, 여러 클래스에서 동일한 기능을 구현할 수 있도록 도와줍니다.

주요 특징

  • 다중 상속 가능: 자바에서 하나의 클래스만 상속이 가능한 것과 달리, 인터페이스는 여러 개를 동시에 구현할 수 있습니다.
  • 추상 메서드만 포함: 자바 8 이전까지는 인터페이스 안에 모든 메서드는 구현되지 않은 추상 메서드로만 이루어져 있었습니다. 자바 8 이후부터는 디폴트 메서드와 정적 메서드도 포함할 수 있습니다.
  • 상속이 아닌 구현: 클래스가 인터페이스를 상속받는 것이 아니라 구현(implements)합니다.
  • 필드 선언 불가: 인터페이스에는 필드(변수)를 가질 수 없으며, 상수만 선언 가능합니다.
    예시
interface Flyable {
    void fly();  // 추상 메서드
}

class Bird implements Flyable {
    @Override
    public void fly() {
        System.out.println("The bird is flying");
    }
}

Bird 클래스는 Flyable 인터페이스를 구현(implements)하여 fly() 메서드를 정의합니다.

3. 추상 클래스와 인터페이스의 차이점

추상 클래스
목적 : 상속을 통해 기능을 공유하고 확장하는 데 사용
상속 및 구현 : 하나의 클래스만 상속 가능
메서드 포함 여부 : 추상 메서드와 일반 메서드 모두 포함 가능
필드(변수) 포함 여부 : 필드(인스턴스 변수) 포함 가능
객체 생성 여부 : 직접 객체 생성 불가

인터페이스
목적 : 특정 기능을 구현하도록 강제하는 데 사용
상속 및 구현 : 다중 상속(다중 구현) 가능
메서드 포함 여부 : 추상 메서드만 포함 (자바 8 이후 디폴트, 정적 메서드 가능)
필드(변수) 포함 여부 : 필드는 가질 수 없으며 상수만 선언 가능
객체 생성 여부 : 인터페이스는 객체를 생성할 수 없음

추가적인 차이점

  • 추상 클래스는 IS-A 관계: 추상 클래스는 상속 관계를 통해 계층 구조를 만듭니다. 자손 클래스는 부모 클래스의 특징을 상속받아 확장하는 구조를 가지며, 이는 "A는 B이다"(IS-A)라는 관계를 나타냅니다.
  • 예: Dog 클래스는 Animal 클래스의 자식이므로, "Dog는 Animal이다"라는 관계가 성립합니다.
  • 인터페이스는 HAS-A 관계: 인터페이스는 특정 기능을 구현하는 것을 목적으로 하며, 다중 구현을 지원하므로 "A는 B 기능을 가지고 있다"(HAS-A)라는 관계로 설명할 수 있습니다.
    예: Bird 클래스는 Flyable 인터페이스를 구현하므로, "Bird는 날 수 있는 기능을 가지고 있다"라고 표현할 수 있습니다.

4. 언제 추상 클래스를 사용하고 언제 인터페이스를 사용할까?

  • 추상 클래스 사용 시점: 클래스 간에 공통된 속성이나 메서드를 제공하며, 이를 상속을 통해 확장하는 경우에 적합합니다. 추상 클래스는 일부 메서드의 기본 구현을 제공할 수 있기 때문에 여러 클래스에서 공통으로 사용되는 코드가 있을 때 유용합니다.

  • 예: 여러 동물 클래스가 공통된 sleep() 메서드를 가지되, 각각의 동물이 고유의 sound()를 가지도록 하는 경우.

  • 인터페이스 사용 시점: 특정 기능을 여러 클래스에서 구현하도록 강제하는 경우에 적합합니다. 자바에서 클래스는 하나만 상속할 수 있지만, 인터페이스는 여러 개를 구현할 수 있기 때문에 다중 상속의 유연성을 제공합니다.

  • 예: 여러 객체가 Flyable 인터페이스를 구현하여 각각의 방식으로 날 수 있는 기능을 제공하도록 할 때.

결론

추상 클래스와 인터페이스는 자바에서 매우 중요한 객체 지향 설계 도구입니다. 추상 클래스는 공통된 속성 및 메서드를 제공하고, 상속을 통해 이를 확장할 때 사용되며, 인터페이스는 다중 상속이 가능하고 특정 기능을 구현하도록 강제하는 역할을 합니다. 각 상황에 맞는 설계를 통해 더 효율적인 코드 구조를 만들 수 있습니다.


Q1: 추상 클래스와 인터페이스를 함께 사용할 수 있는 사례는 어떤 경우일까요?

추상 클래스와 인터페이스를 함께 사용할 수 있는 대표적인 사례는 다중 상속의 제한을 극복하면서 클래스 계층 구조를 만들고, 동시에 여러 기능을 구현해야 하는 경우입니다. 자바에서 클래스는 하나만 상속할 수 있지만, 인터페이스는 여러 개를 구현할 수 있기 때문에, 상속을 통해 공통된 속성과 동작을 제공하면서 다양한 기능을 구현하는 방식으로 사용할 수 있습니다.

예를 들어, 아래와 같은 경우가 있습니다:

abstract class Vehicle {
    abstract void move();
    void stop() {
        System.out.println("Vehicle stopped");
    }
}

interface Flyable {
    void fly();
}

class FlyingCar extends Vehicle implements Flyable {
    @Override
    void move() {
        System.out.println("The car is moving");
    }

    @Override
    public void fly() {
        System.out.println("The car is flying");
    }
}

위 예시에서 FlyingCar는 Vehicle 추상 클래스를 상속받아 공통된 기능인 move()와 stop()을 제공받으며, 동시에 Flyable 인터페이스를 구현하여 날 수 있는 기능을 추가로 가지게 됩니다.

따라서 기본 동작은 추상 클래스를 통해 상속받고, 부가적인 기능은 인터페이스를 통해 구현하는 방식으로 설계할 수 있습니다.

Q2: 자바 8 이후 인터페이스에 도입된 디폴트 메서드와 정적 메서드는 어떤 장점을 가지고 있나요?

자바 8에서 도입된 디폴트 메서드와 정적 메서드는 인터페이스의 기능을 확장하며, 개발자에게 다음과 같은 장점을 제공합니다:

디폴트 메서드의 장점:

  • 하위 호환성 유지: 기존 인터페이스에 새로운 메서드를 추가할 때, 모든 구현 클래스가 그 메서드를 구현해야 하는 문제를 해결하기 위해 디폴트 메서드가 도입되었습니다. 디폴트 메서드를 사용하면 기존 인터페이스에 새로운 기능을 추가하면서, 구현 클래스에서 이를 강제로 구현하지 않아도 됩니다.
  • 기본 구현 제공: 인터페이스에서 기본적인 메서드 구현을 제공할 수 있습니다. 즉, 인터페이스를 구현하는 클래스는 필요할 경우 디폴트 메서드를 오버라이드할 수 있지만, 그렇지 않은 경우 기본 구현을 사용할 수 있습니다.
    예시:
interface Walkable {
    default void walk() {
        System.out.println("Walking...");
    }
}

class Human implements Walkable {
    // 필요하면 walk() 메서드를 오버라이드할 수 있음
}

정적 메서드의 장점:

  • 유틸리티 메서드 제공: 인터페이스 자체에서 유틸리티 메서드(객체와 관련 없는 보조적인 기능)를 제공할 수 있습니다. 이는 외부에서 인터페이스의 인스턴스를 만들지 않고도, 클래스 메서드처럼 사용할 수 있습니다.
  • 예를 들어, java.util.Collections 클래스에서 제공하는 정적 메서드들처럼, 특정 기능을 공통적으로 제공하는 유틸리티 메서드를 인터페이스에 포함시킬 수 있습니다.
    예시:
interface MathOperations {
    static int add(int a, int b) {
        return a + b;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(MathOperations.add(2, 3));  // 5
    }
}

결론: 디폴트 메서드는 인터페이스의 하위 호환성을 유지하면서 기본 구현을 제공할 수 있는 장점이 있으며, 정적 메서드는 객체와 무관한 유틸리티 기능을 인터페이스에 포함시킬 수 있는 기능을 제공합니다.

Q3: 추상 클래스와 인터페이스의 사용 시 가장 큰 차이는 어떤 상황에서 나타날까요?

추상 클래스와 인터페이스의 가장 큰 차이는 상속과 구현 방식의 차이에서 나타납니다. 그 차이는 구체적으로 다음과 같은 상황에서 두드러집니다:

클래스의 상속 여부:

  • 자바는 하나의 클래스만 상속할 수 있지만, 인터페이스는 여러 개를 구현할 수 있습니다. 즉, 만약 하나의 클래스에서 다양한 기능을 구현해야 한다면 인터페이스를 사용해야 합니다.

  • 반대로, 공통된 속성이나 동작을 상속받아 확장해야 한다면 추상 클래스를 사용하는 것이 더 적합합니다.
    예시:

  • 추상 클래스 사용: 여러 동물 클래스가 Animal 추상 클래스를 상속받아 공통 동작(sleep())을 가지면서, 각 동물마다 고유의 sound() 메서드를 구현.

  • 인터페이스 사용: 여러 객체가 동일한 기능(예: Flyable 인터페이스의 fly() 메서드)을 구현해야 할 때, 다중 인터페이스를 사용하여 다양한 기능을 추가.

기본 구현의 유무:

-추상 클래스는 일부 메서드의 기본 구현을 포함할 수 있지만, 인터페이스는 기본적으로 추상 메서드만 포함합니다. 따라서 공통된 동작을 재사용하고 싶을 때는 추상 클래스가 더 적합합니다.
-예를 들어, 여러 클래스가 같은 메서드의 기본 구현을 필요로 할 때, 추상 클래스를 사용하여 그 기본 구현을 제공하는 것이 더 효율적일 수 있습니다.

설계 목적:

  • 추상 클래스는 "IS-A" 관계를 나타냅니다. 즉, 자식 클래스가 부모 클래스의 특성을 상속받고, 이를 확장하는 개념입니다.
  • 인터페이스는 "HAS-A" 관계로 기능을 추가하는 용도로 사용됩니다. 예를 들어, Car는 Vehicle이지만 Flyable을 구현할 수도 있습니다.

결론: 추상 클래스는 공통된 동작을 상속받아 확장할 때 적합하며, 인터페이스는 다양한 기능을 여러 클래스에 적용하고 싶을 때 적합합니다.

profile
백엔드에서 서버엔지니어가 된 사람

0개의 댓글