Back_End/Java

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

10Biliion 2024. 12. 11. 16:57
반응형

 

 

왜 생겨났을까? "역할"과 "구현"의 분리

코딩을 하다 보면 비슷한 기능을 가진 여러 클래스를 만들어야 할 때가 많습니다. 예를 들어, 게임에 등장하는 몬스터를 만든다고 가정해 봅시다. 고블린, 오크, 드래곤은 모두 '공격'이라는 기능을 가지고 있죠.

초보 개발자라면 아마 각 클래스에 attack()이라는 메서드를 개별적으로 만들 겁니다. 하지만 이렇게 하면 문제가 생깁니다.

  • 고블린의 공격 방식은 단검 던지기, 오크몽둥이 휘두르기, 드래곤불 뿜기 등 각각의 구현 내용이 다릅니다.
  • 만약 게임의 새로운 규칙을 추가해서 '모든 몬스터는 독에 걸릴 수 있다'는 기능을 넣으려면, 모든 몬스터 클래스에 poison() 메서드를 추가해야 합니다. 몬스터 종류가 많아질수록 수정해야 할 곳이 기하급수적으로 늘어나겠죠.

이 문제를 해결하기 위해 프로그래밍 세계에서는 "역할(What)"과 "구현(How)"을 분리하는 개념을 도입했습니다. 인터페이스와 추상 클래스는 이 분리를 가능하게 해줍니다.

 

인터페이스 (Interface) - "역할"을 정의하는 계약서

인터페이스는 클래스가 무엇을 할 수 있는지를 정의하는 청사진 또는 계약서와 같습니다.

  • 정의: 메서드의 이름만 있고, 구현 내용(중괄호 {})이 없는 추상 메서드들로만 이루어져 있습니다. (자바 8 이후 default, static 메서드 추가)
  • 특징:
    • 다중 상속 가능: 여러 개의 인터페이스를 동시에 implements할 수 있습니다. 예를 들어, 공격도 할 수 있고, 날 수도 있는 몬스터를 만들 수 있습니다.
    • 강제성: 인터페이스를 implements하는 클래스는 인터페이스에 정의된 모든 메서드를 반드시 구현해야 합니다. 이를 통해 일관성을 유지할 수 있습니다.
  • 사용 예시:
public interface Flyable {
    void fly();
}

public interface Swimmable {
    void swim();
}

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

public class Fish implements Swimmable {
    @Override
    public void swim() {
        System.out.println("Fish is swimming.");
    }
}

public class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("Duck is flying.");
    }

    @Override
    public void swim() {
        System.out.println("Duck is swimming.");
    }
}

 

public class Main {
    public static void main(String[] args) {
        Flyable bird = new Bird();
        bird.fly(); // Bird is flying.

        Swimmable fish = new Fish();
        fish.swim(); // Fish is swimming.

        Duck duck = new Duck();
        duck.fly();  // Duck is flying.
        duck.swim(); // Duck is swimming.
    }
}

 

추상 클래스 (Abstract Class) - "공통적인 뼈대"를 제공하는 설계도

추상 클래스는 미완성된 설계도와 같습니다. 몇몇 중요한 부분은 미리 만들어 놓았지만, 특정 부분은 자식 클래스에서 직접 완성하라고 남겨둔 형태죠.

  • 정의: abstract 키워드가 붙은 클래스로, 추상 메서드일반 메서드를 모두 가질 수 있습니다.
  • 특징:
    • 단일 상속: 오직 하나의 추상 클래스만 extends할 수 있습니다.
    • 일반 메서드와 멤버 변수 포함: 공통적으로 사용되는 기능은 미리 구현해 놓아 코드의 중복을 줄일 수 있습니다.
    • 객체 생성 불가: 추상 클래스는 불완전하기 때문에 직접 객체를 만들 수 없습니다.
  • 사용 예시:
public abstract class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    // 추상 메서드
    public abstract void makeSound();

    // 일반 메서드
    public void eat() {
        System.out.println(name + " is eating.");
    }
}

public class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    @Override
    public void makeSound() {
        System.out.println("Woof Woof!");
    }
}

public class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }
}

 

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog("Buddy");
        dog.makeSound(); // Woof Woof!
        dog.eat();       // Buddy is eating.

        Animal cat = new Cat("Kitty");
        cat.makeSound(); // Meow!
        cat.eat();       // Kitty is eating.
    }
}

 

인터페이스와 추상 클래스, 무엇이 다른가?

구분 인터페이스 (Interface) 추상 클래스 (Abstract Class)
목적 '역할' 정의 (What to do) '공통 뼈대' 제공 (What and How)
추상 메서드 추상 메서드만 가질 수 있음 (자바 8 이후 default, static 메서드 추가) 추상 메서드와 일반 메서드 모두 가질 수 있음
일반 메서드 default나 static으로만 가능 자유롭게 포함 가능
다중 상속 여러 개를 implements 할 수 있음 한 개만 extends 할 수 있음
사용 시점 관련 없는 클래스들이 공통된 기능을 가져야 할 때 (ex. 공격 기능, 비행 기능) 클래스들 간에 공통된 속성이나 기능이 많을 때 (ex. 모든 동물은 '이름'과 '나이'를 가짐)
 

 

  • 인터페이스: "이 클래스는 이런 기능을 할 수 있다"라고 역할을 명시하고 싶을 때 사용하세요. 여러 클래스에 걸쳐 기능의 일관성을 강제해야 할 때 유용합니다.
  • 추상 클래스: "이 클래스들의 공통적인 골격은 이렇게 만들자"라고 미리 설계하고 싶을 때 사용하세요. 기능뿐만 아니라, **공통된 속성(필드)**도 함께 물려주고 싶을 때 적합합니다.

 

반응형