Dandy Now!
  • [Java] 추상 클래스(Abstract Class)와 인터페이스(Interface)
    2024년 02월 02일 11시 01분 12초에 업로드 된 글입니다.
    작성자: DandyNow
    728x90
    반응형

    추상 클래스와 인터페이스는 자바에서 다형성(Polymorphism)을 구현하고, 객체 지향 설계 원칙을 적용하는 데 사용되는 중요한 개념이다. 둘 다 클래스 간의 계약(Contract)을 정의하고, 공통된 기능을 제공하는 데 사용되지만, 몇 가지 핵심적인 차이점을 가지고 있다.


    추상 클래스(Abstract Class)와 인터페이스(Interface)

    1. 추상 클래스(Abstract Class)

    1.1 추상 클래스의 정의

    추상 클래스는 이름 그대로 추상 메서드(Abstract Method)를 하나 이상 포함하고 있거나, abstract 키워드로 선언된 클래스이다. 추상 메서드는 선언만 있고 구현(body)이 없는 메서드를 말한다. 추상 클래스는 미완성된 클래스이기 때문에 직접 객체를 생성할 수 없다. 반드시 하위 클래스에서 상속받아 추상 메서드를 구현(오버라이딩)해야만 객체를 생성할 수 있다.

    1.2 추상 클래스의 특징

    • 추상 메서드 포함 가능: 0개 이상의 추상 메서드를 가질 수 있다. (즉, 추상 메서드가 없어도 추상 클래스가 될 수 있다.)
    • 일반 메서드 포함 가능: 일반 메서드(구현부가 있는 메서드)와 멤버 변수(필드)를 가질 수 있다.
    • 생성자 가질 수 있음: 생성자를 가질 수 있지만, 직접 객체를 생성할 수 없으므로 new 연산자로 호출될 수는 없다. 대신 하위 클래스에서 super()를 통해 호출될 수 있다.
    • 단일 상속: 자바에서는 클래스의 단일 상속만 허용하므로, 추상 클래스를 상속받으면 다른 클래스를 동시에 상속받을 수 없다.
    • abstract 키워드: 클래스와 메서드 선언 시 abstract 키워드를 사용해야 한다.

    1.3 추상 클래스의 목적

    • 공통된 기능 구현: 하위 클래스들이 공유할 수 있는 공통적인 기능과 속성을 미리 구현해 제공할 수 있다.
    • 강제적인 구현: 하위 클래스에서 반드시 구현해야 할 메서드(추상 메서드)를 강제하여, 일관된 동작을 보장한다.
    • 'IS-A' 관계: 추상 클래스는 'A는 B이다 (A IS A B)'와 같은 강력한 상속 관계를 나타낼 때 적합하다(A는 자식 클래스, B는 부모 클래스).

    1.4 추상 클래스의 예시: Animal

    모든 동물은 소리를 내지만, 구체적인 소리는 동물마다 다르다고 가정해보자.

    // 추상 클래스: Animal (동물)
    public abstract class Animal {
        private String name; // 일반 멤버 변수
    
        public Animal(String name) { // 생성자
            this.name = name;
        }
    
        // 일반 메서드 (구현부가 있다)
        public void eat() {
            System.out.println(name + "가 먹고 있다.");
        }
    
        // 추상 메서드 (구현부가 없다, 하위 클래스에서 반드시 구현해야 한다.)
        public abstract void makeSound();
    
        // 일반 메서드 (추상 메서드와 함께 사용 가능)
        public String getName() {
            return name;
        }
    }
    
    // 추상 클래스를 상속받은 하위 클래스: Dog
    public class Dog extends Animal {
        public Dog(String name) {
            super(name);
        }
    
        @Override // 추상 메서드 구현 (오버라이딩)
        public void makeSound() {
            System.out.println(getName() + "가 멍멍 짖는다.");
        }
    }
    
    // 추상 클래스를 상속받은 하위 클래스: Cat
    public class Cat extends Animal {
        public Cat(String name) {
            super(name);
        }
    
        @Override // 추상 메서드 구현 (오버라이딩)
        public void makeSound() {
            System.out.println(getName() + "가 야옹거린다.");
        }
    }
    
    // 사용 예시
    public class AbstractClassExample {
        public static void main(String[] args) {
            // Animal animal = new Animal("동물"); // 에러! 추상 클래스는 객체 생성 불가
    
            Animal dog = new Dog("바둑이");
            dog.eat(); // 일반 메서드 호출
            dog.makeSound(); // 오버라이딩된 메서드 호출
    
            Animal cat = new Cat("나비");
            cat.eat();
            cat.makeSound();
        }
    }

    2. 인터페이스(Interface)

    2.1 인터페이스의 정의

    인터페이스는 클래스가 구현해야 할 메서드들의 집합을 정의하는 완전히 추상화된 타입이다. 인터페이스는 모든 메서드가 기본적으로 public abstract이고, 모든 필드는 public static final이다. 인터페이스는 클래스가 어떤 기능을 수행할 수 있는지에 대한 계약을 명시한다. 자바 8부터는 default 메서드와 static 메서드를 가질 수 있게 되어 기능이 확장되었다.

    2.2 인터페이스의 특징

    • 모든 메서드가 추상 메서드 (기본): 자바 7까지는 모든 메서드가 추상 메서드였다. 자바 8부터 default 메서드와 static 메서드를 가질 수 있다.
    • 모든 필드는 상수: 모든 필드는 public static final로 자동 선언된다.
    • 다중 상속(구현): 자바에서 클래스의 다중 상속은 불가능하지만, 인터페이스는 여러 개를 동시에 구현(implement)할 수 있다. (다중 구현)
    • interface 키워드: 선언 시 interface 키워드를 사용해야 한다.
    • 객체 생성 불가: 인터페이스 또한 직접 객체를 생성할 수 없다. 반드시 다른 클래스에서 구현(implement)해야 한다.

    2.3 인터페이스의 목적

    • 기능의 명세: 클래스가 어떤 기능을 제공해야 하는지 표준적인 방법을 제시한다.
    • 다중 상속의 대안: 자바의 단일 상속 제한을 극복하고, 여러 기능적 특성을 클래스에 부여할 수 있게 한다.
    • 느슨한 결합(Loose Coupling): 인터페이스를 통해 객체 간의 의존성을 낮춰, 시스템의 유연성과 확장성을 높인다.
    • 'CAN-DO' 관계: 인터페이스는 'A는 B를 할 수 있다 (A CAN DO B)'와 같은 기능적 관계를 나타낼 때 적합하다(A는 클래스, B는 인터페이스).

    2.4 인터페이스의 예시: Flyable

    날 수 있는 기능을 가진 객체들을 정의하는 Flyable 인터페이스를 생각해보자.

    // 인터페이스: Flyable (날 수 있는)
    public interface Flyable {
        // 모든 메서드는 public abstract 이다 (자바 8 이전)
        void fly(); // 날아가는 기능
        void land(); // 착륙하는 기능
    
        // 자바 8부터 가능한 default 메서드 (구현부가 있다)
        default void reportStatus() {
            System.out.println("비행 상태를 보고한다.");
        }
    }
    
    // 인터페이스를 구현한 클래스: Bird
    public class Bird implements Flyable {
        @Override
        public void fly() {
            System.out.println("새가 날개를 퍼덕이며 날아간다.");
        }
    
        @Override
        public void land() {
            System.out.println("새가 나뭇가지에 착륙한다.");
        }
    }
    
    // 인터페이스를 구현한 클래스: Airplane
    public class Airplane implements Flyable {
        @Override
        public void fly() {
            System.out.println("비행기가 엔진을 가동하며 이륙한다.");
        }
    
        @Override
        public void land() {
            System.out.println("비행기가 활주로에 착륙한다.");
        }
    }
    
    // 사용 예시
    public class InterfaceExample {
        public static void main(String[] args) {
            Flyable bird = new Bird();
            bird.fly();
            bird.land();
            bird.reportStatus(); // default 메서드 호출
    
            Flyable airplane = new Airplane();
            airplane.fly();
            airplane.land();
            airplane.reportStatus(); // default 메서드 호출
        }
    }

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

    특징 추상 클래스(Abstract Class) 인터페이스(Interface)
    목적 'IS-A' 관계 (유사한 객체들의 공통 기능/속성 제공) 'CAN-DO' 관계 (객체가 수행할 수 있는 기능 명세)
    객체 생성 직접 생성 불가, 상속받아 구현해야 생성 가능 직접 생성 불가, 구현(implement)해야 생성 가능
    추상 메서드 0개 이상 (있을 수도, 없을 수도 있다) 자바 7까지는 모두 추상 메서드, 자바 8+ 부터 default/static 메서드 가능
    일반 메서드 가질 수 있다 (구현부가 있는 메서드) 자바 8+ 부터 default/static 메서드 가능 (그 외에는 불가)
    필드(변수) 일반 변수 가질 수 있다 public static final 상수만 가질 수 있다
    접근 제어자 public, protected, private 모두 사용 가능 public만 사용 가능 (필드도 public static final이 기본)
    상속/구현 extends 키워드로 단일 상속 implements 키워드로 다중 구현 가능

    4. 언제 무엇을 사용할까?

    • 추상 클래스:
      • 밀접하게 관련된 클래스들 사이에 공통적인 행동과 상태를 공유하고 싶을 때.
      • 'IS-A' 관계가 명확하고, 일반적인 기능들을 미리 구현해두고 싶을 때.
      • 템플릿 메서드 패턴과 같이 특정 로직의 흐름을 정의하고, 일부 단계만 하위 클래스에서 구현하도록 강제하고 싶을 때.
      • 새로운 메서드를 추가할 때, 하위 클래스에서 반드시 구현하지 않아도 되는 경우 (일반 메서드로 추가).
    • 인터페이스:
      • 서로 관련 없는 클래스들이 특정 기능을 공통적으로 수행해야 함을 명세하고 싶을 때.
      • 'CAN-DO' 관계가 명확하고, 다양한 타입의 객체에 동일한 기능을 부여하고 싶을 때.
      • 다중 상속의 효과를 내고 싶을 때 (한 클래스가 여러 인터페이스를 구현).
      • 클래스 간의 느슨한 결합을 극대화하여 시스템의 유연성과 확장성을 높이고 싶을 때.
      • 새로운 메서드를 추가할 때, 기존 구현 클래스에 영향을 주지 않으면서 기능을 확장하고 싶을 때 (default 메서드 사용).
    728x90
    반응형
    댓글