[Practical-Java] 3. Abstract Class와 Interface


추상 클래스(Abstract Class)인터페이스(Interface)의 차이점 및 용도로는 추상 클래스와 인터페이스는 모두 다형성(polymorphism) 을 지원하고, 코드 재사용성을 높이며, 설계 원칙을 명확히 하기 위해 사용되지만 목적, 사용 방법 및 기능에 따라 차이가 있다.


1. 추상 클래스와 인터페이스

추상 클래스와 인터페이스는 추상화를 통해 객체지향 설계에서 공통적인 동작이나 계약을 정의하는 데 사용된다. 그리고 추상 클래스와 인터페이스 자체로는 직접 객체화 할 수 없고 구현 클래스를 통해서 객체 생성이 가능하다.


① 공통된 목적

추상화 제공:

  • 둘 다 직접적인 객체 생성이 불가능하며, 하위 클래스나 구현체를 통해 동작이 구체화됩니다.
    • 추상 클래스는 “공통된 속성과 동작”의 기본 틀을 제공합니다.
    • 인터페이스는 “구현해야 할 행동”의 계약을 정의합니다.


② 다형성(Polymorphism) 지원

참조 타입으로 사용 가능:

  • 추상 클래스와 인터페이스 모두 부모 타입(추상 클래스 또는 인터페이스)으로 참조하고, 하위 클래스나 구현체에서 다형성을 구현할 수 있다.
abstract class Animal {
    abstract void makeSound();
}

interface Flyable {
    void fly();
}

class Bird extends Animal implements Flyable {
    @Override
    void makeSound() {
        System.out.println("Chirp!");
    }

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

public class Main {
    public static void main(String[] args) {
        Animal animal = new Bird();     // 추상 클래스 타입으로 참조
        animal.makeSound();

        Flyable flyable = new Bird();   // 인터페이스 타입으로 참조
        flyable.fly();
    }
}


③ 추상 메서드 포함

구현을 강제:

  • 추상 클래스와 인터페이스 모두 추상 메서드를 포함하여 구현체에 특정 메서드의 구현을 강제합니다.


④ 구현체에서 확장/구현 필요

  • 추상 클래스는 상속을 통해 확장해야 하고,
  • 인터페이스는 구현을 통해 구체적인 동작을 정의해야 합니다.


⑤ 상속 및 구현 관계에서 사용

추상 클래스: 클래스 간의 “is-a” 관계

  • 하위 클래스가 상위 클래스의 일종이라는 것을 의미
  • 추상 클래스의 공통 속성(필드)과 동작(메서드)을 하위 클래스에서 그대로 사용하거나, 필요에 따라 확장(재정의)
  • 추상 클래스는 공통된 속성과 동작을 공유하면서, 특정 클래스 계층의 멤버임을 나타내기 위해 사용

인터페이스: 클래스 간의 “can-do” 관계

  • 클래스가 특정 기능을 수행할 수 있음을 보장하는 계약을 의미
  • 인터페이스는 특정 행동(메서드)을 구현하도록 클래스를 강제하거나 여러 동작을 조합
  • 클래스는 인터페이스를 구현하여 “나는 이 행동을 할 수 있다”는 것을 나타냄


⑥ 설계 원칙 준수

SOLID 원칙을 지원:

  • OCP(개방-폐쇄 원칙): 추상 클래스와 인터페이스는 코드 확장성에 기여
  • DIP(의존성 역전 원칙): 둘 다 상위 수준의 설계를 통해 구현체와 느슨한 결합(loose coupling)을 지원


⑦ 객체지향 프로그래밍(OOP)에서 필수 요소

  • 둘 다 캡슐화(Encapsulation), 다형성(Polymorphism), 추상화(Abstraction)를 구현하는 데 중요한 역할
  • 공통 동작을 정의하여 코드 재사용성을 높이고, 유지보수에 용이하게 함



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

  • 추상 클래스는 멤버 변수를 가질 수 있지만 인터페이스는 멤버 변수를 가질 수 없다. 물론 인터페이스도 static으로 정의된 변수를 내부적으로 선언할 수 있지만 멤버 변수는 선언할 수 없다.
  • 클래스를 구현할때 오직 하나의 클래스만을 상속 받을 수 있는 반면에 인터페이스는 여러 개를 상속받거나 구현 할 수 있다.

멤버 변수는 그 객체의 속성을 담기 위한 용도로 사용하는데 메서드 내에 선언 된 지역 변수는 메서드가 종료되면 그 상태를 잃어버리지만 클래스의 멤버 변수는 객체가 유지되는 동안은 속성 상태를 유지하거나 변경할 수도 있는데 인터페이스에 멤버변수가 존재하지 않는다.


추상 클래스와 인터페이스의 선택 기준

1 . 공통 로직 제공 여부

  • 공통 로직과 속성을 공유하려면 추상 클래스
  • 구현 클래스 간 공통된 동작을 강제하려면 인터페이스

2 . 다중 상속 필요 여부

  • 단일 상속으로 계층구조가 필요하면 추상 클래스
  • 다중 상속으로 다중 행동을 조합해야 한다면 인터페이스

3 . 설계의 유연성

  • 인터페이스는 느슨한 결합으로 기능 확장에 적합.
  • 추상 클래스는 강한 결합으로 계층 구조를 명확히 하고 재사용성 증가.



결론

결론은 필드 선언 가능 여부와 다중 구현 가능성 차이로
추상 클래스는 상태(필드)를 다루고 기본 구현을 제공하는 데 중점 인터페이스는 동작(메서드)를 정의하고 다중 구현을 지원하는 데 중점

추상 클래스: “IS-A” 관계

  • 클래스 간 상속을 통한 공통 로직과 속성을 공유할 때 사용
  • 상속 계층에서 공통 동작과 상태를 정의할 때 사용
  • 하위 클래스가 반드시 특정 메서드를 구현하도록 강제하며, 일부는 공통 구현 제공

인터페이스: “CAN-DO” 관계

  • 다중 상속을 통한 행동(기능)의 계약을 정의할 때 사용
  • 클래스를 특정 행동을 강제적으로 구현하도록 만들 때 사용
  • 다중 상속이 필요할 때 사용