김코딩

객체를 못 만드는 클래스가 있다고요? 그게 바로 추상클래스 본문

TIL

객체를 못 만드는 클래스가 있다고요? 그게 바로 추상클래스

김코딩딩 2025. 4. 23. 18:55
추상클래스를 이해하기 전에, 우선 다형성이 어떻게 쓰이는지 간단한 예시부터 살펴보겠습니다.

다형성 활용

 

Animal

public class Animal {
    public void sound() {
        System.out.println("동물 울음 소리");
    }
}


Dog

public class Dog extends Animal {
    public void sound() {
        System.out.println("멍멍");
    }
}

 

Cat

public class Cat extends Animal {
    public void sound() {
        System.out.println("냐옹");
    }
}

 

Cow

public class Cow extends Animal {
    public void sound() {
        System.out.println("음메");
    }
}

 

AnimalSoundMain

public class AnimalSoundMain {
    public static void main(String[] args) {
        Animal[] animals = {new Dog(), new Cat(), new Cow()};

        for (int i = 0; i < animals.length; i++) {
            sound(animals[i]);
        }
    }

    private static void sound(Animal animal) {
        System.out.println("동물 울음소리 테스트 시작");
        animal.sound();
        System.out.println("동물 울음소리 테스트 끝");
    }
}

위 코드는 Animal이라는 부모 클래스를 만들고, Dog, Cat, Cow 같은 자식 클래스들이 이를 상속받아
같은 타입(Animal) 으로 동물들의 울음소리를 처리할 수 있게 해주는 다형성의 예입니다.

그런데 이 예제에서 우리는 Animal이라는 클래스를 직접 new 해서 쓸 일이 없습니다.
Animal은 그저 공통된 구조와 규칙을 정의해주는 용도로만 사용될 뿐이죠.

그럼 이런 상황에서,
"이 클래스는 설계도로만 쓰이고, 객체로는 쓰이지 않도록" 만들 수는 없을까요?

바로 그럴 때 등장하는 것이 추상클래스 (abstract class) 입니다.

추상클래스는 객체를 만들 수 없는 클래스입니다.
new로 인스턴스를 만들려고 하면 컴파일 오류가 납니다.
하지만 자식 클래스는 상속을 통해 이 틀을 사용할 수 있죠.

그리고 이 틀 안에
"이 메서드는 자식이 반드시 구현해야 해!" 라고 강제하고 싶을 때,
거기에 쓰이는 게 바로 추상 메서드(abstract method)입니다.

결국, 추상클래스는 "공통 설계도 역할 + 객체 생성 금지",
추상 메서드는 "자식에게 꼭 구현하라고 강제" 이 두 가지 역할을 함께 수행하는 거죠!


추상 클래스란?

객체를 생성할 수 있는 클래스를 실체 클래스라고 한다면, 이 클래스들의 공통적인 필드나 메소드를 추출해서 선언한 클래스를 추상클래스라고 한다. 출처: 『이것이 자바다』

 


추상 클래스 선언

클래스 선언에 abstract 키워드를 붙이면 추상 클래스 선언이 됩니다. 추상 클래스는 new 연산자를 이용해서 객체를 직접 만들지 못하고 상속을 통해 자식 클래스만 만들 수 있습니다.

public abstract class 클래스명 {
    //필드
    //생성자
    //메소드
}

 


추상 메서드

부모 클래스를 상속 받는 자식 클래스가 반드시 오버라이딩 해야 하는 메서드를 부모 클래스에 정의할 수 있습니다. 이것을 추상 메서드라고 합니다. 추상 메서드는 이름 그대로 추상적인 개념을 제공하는 메서드입니다. 따라서 실체가 존재하지 않고, 메서드 바디가 없습니다.

public abstract class AbstractAnimal {
    public abstract void sound(); //추상메서드는 메서드 바디를 생성할 수 없다.
}
  • 추상 메서드는 선언할 때 메서드 앞에 추상이라는 의미의 abstract 키워드를 붙여주면 됩니다.
  • 추상 메서드가 하나라도 있는 클래스는 추상 클래스로 선언해야 한다.

        그렇지 않으면 컴파일 오류가 발생합니다.

        추상 메서드는 메서드 바디가 없습니다. 따라서 작동하지 않는 메서드를 가진 불완전한 클래스로 볼 수 있습니다. 

  • 추상 메서드는 상속 받는 자식 클래스가 반드시 오버라이딩 해서 사용해야 합니다.
  • 추상 메서드는 기존 메서드와 완전히 같습니다. 다만 메서드 바디가 없고, 자식 클래스가 해당 메서드를 반드시 오버라이딩 해야 한다는 제약이 추가된 것입니다.

이전의 예제를 추상클래스를 사용하여 다시 보여드리겠습니다.

 

AbstractAnimal

public abstract class AbstractAnimal {
    public abstract void sound(); //추상메서드는 메서드 바디를 생성할 수 없다.

    public void move() {
        System.out.println("동물이 움직임");
    }
}

 

Dog

public class Dog extends AbstractAnimal {
    @Override
    public void sound() {
        System.out.println("멍멍");
    }
}

 

Cat

public class Cat extends AbstractAnimal {
    @Override
    public void sound() {
        System.out.println("냐옹");
    }
}

 

Cow

public class Cow extends AbstractAnimal {
    @Override
    public void sound() {
        System.out.println("음메");
    }
}

 

AbstractAnimalSoundMain

public class AbstractAnimalSoundMain {
    public static void main(String[] args) {
//        AbstractAnimal abstractAnimal = new AbstractAnimal() //추상클래스는 객체 생성 불가
        
        Dog dog = new Dog();
        Cat cat = new Cat();
        Cow cow = new Cow();

        cat.sound();
        cat.move();

        soundAnimal(cat);
    }

    private static void soundAnimal(AbstractAnimal animal) {
        System.out.println("동물 소리 테스트 시작");
        animal.sound();
        System.out.println("동물 소리 테스트 종료");
    }
}

 

위 코드는 앞에서 봤던 다형성 예제를 추상클래스와 추상 메서드를 활용해 리팩터링한 버전입니다.

코드에서 볼 수 있듯이, 추상 클래스 AbstractAnimal에는
추상 메서드 sound()가 선언되어 있고,
이를 상속받은 Dog, Cat, Cow 클래스는 반드시 이 메서드를 오버라이딩해야 합니다.
그렇지 않으면 컴파일 오류가 발생합니다.

또 AbstractAnimalSoundMain 클래스에서 확인할 수 있듯이,
추상 클래스는 new 키워드로 객체를 직접 생성할 수 없습니다.

이렇게 추상클래스를 사용하면 앞에서 겪었던 두 가지 문제를 해결할 수 있습니다:

객체를 만들 필요 없는 클래스를 객체 생성 불가로 막을 수 있고,

오버라이딩 실수도 컴파일 단계에서 강제할 수 있습니다.

추상클래스는 말 그대로 공통의 구조만 정의하는 설계도이며,
추상 메서드는 이 설계도를 상속받은 클래스가 직접 구현해야 하는 약속입니다.

 


순수 추상 클래스

지금까지 살펴본 추상 클래스는 "객체 생성 금지 + 공통 설계도 제공" 역할을 하면서,  
필요하다면 일반 메서드도 포함할 수 있었습니다.

하지만 만약 "모든 메서드가 추상 메서드"로만 구성되어 있다면 어떨까요?


이런 클래스를 우리는 순수 추상 클래스(pure abstract class) 라고 부릅니다.

그리고 바로 이 개념이 자바에서 인터페이스(interface) 로 이어지는 핵심이 됩니다.

이제 다음 글에서는 순수 추상 클래스와 인터페이스의 차이,  
그리고 "왜 인터페이스가 필요한가?"에 대해 이야기해보겠습니다!