Java 다형성 - 추상 클래스

부모와 자식과 같은 상속을 제공되나, 실제 생성 되지 않도록 하는 것이 추상 클래스이다.
추상 클래스는 추상적인 개념만 제공하고, 실체를 갖게 되는 인스턴스가 없다. 상속을 목적으로 사용하고 부모 역할만 담당한다.

추상 클래스

  • 클래스 이름명 앞에서 해당 키워드를 사용할 수 있다.
  • 추상화된 클래스는 기존 클래스와 같다. new 키워드로 직접 인스턴스를 생성할 수 없는 제약이 걸려있다.
abstract class AbsAnimal {...}

추상 메소드

추상 클래스에서 지정된 추상 메소드는 반드시 자식에서 오버라이딩을 해주어야 한다. 추상 메소드는 추상적인 개념만 제공하며, 실체와 내부 코드가 없다.

  • 메소드 선언 시 타입 앞에 abstract 키워드를 붙어준다.
  • 추상 메소드를 사용하려면 해당 클래스가 추상 클래스로 선언되어야 한다.
    • 조건이 안되면 컴파일 오류 발생
    • 메소드 바디가 없음. 작동하지 않는 불완전한 클래스가 되므로 추상 클래스라고 선언해주어야 한다.
  • 추상 메소드는 반드시 상속 받는 자식 클래스에서 오버라이딩하여 사용한다.
    • 조건이 안되면 컴파일 오류 발생
    • 추상 메소드는 반드시 오버라이딩 해야하고 메소드가 없다.
      지역 블록을 만든 경우 컴파일 오류
    • 오버라이딩을 안한 자식 클래스인 경우 추상 클래스로 선언되어야 한다.

추상 메소드는 3가지 특징이다.

  • 기존 메소드와 동일.
  • 메소드 지역블록 바디가 없음.
  • 자식 클래스에서 메소드 오버라이딩 필수
public abstract void sound();

코드 예제

다음과 같이 클래스를 작성하도록 한다.

AbsAnimal.java

package sound;

public abstract class AbsAnimal {

    public abstract String sound();

    public void move() {
        System.out.println("동물 이동하기~");
    }
}
  • AbsAnimal 클래스는 abstract 선언한 추상 클래스이다. 이제 직접 인스턴스를 생성할 수 없다.
  • sound() 추상 메소드로 사용하려면 자식에서 반드시 오버라이딩해서 사용한다.

move() 일반 메소드도 선언할 수 있다.

Dog.java

package sound;

public class Dog extends AbsAnimal {
    @Override
    public String sound() {
        return "멍멍";
    }
}

Cat.java

package sound;

public class Cat extends AbsAnimal {

    @Override
    public String sound() {
        return "냐옹";
    }
}

Bird.java

package sound;

public class Bird extends AbsAnimal {
    @Override
    public String sound() {
        return "짹짹";
    }
}

AbsAnimalMain.java

package sound;

public class AbsAnimalMain {
    public static void main(String[] args) {

        AbsAnimal[] animals = { new Dog(), new Cat(), new Bird() };
        for (AbsAnimal animal : animals) {
            soundAnimal(animal);
        }

    }

    private static void soundAnimal(AbsAnimal animal) {
        System.out.println("울음 소리: " + animal.sound());
        animal.move();
    }
}

실행결과

울음 소리: 멍멍
동물 이동하기~
울음 소리: 냐옹
동물 이동하기~
울음 소리: 짹짹
동물 이동하기~

이제 컴파일 오류를 발생 시키는 케이스를 살펴 보도록 한다.

추상 클래스 인스턴스 생성 케이스 - 컴파일 오류

public class AnimalMain {
    public static void main(String[] args) {
        AbsAnimal animal = new AbsAnimal; // 컴파일 오류
    }
}

추상 클래스를 강제로 생성한 경우 오류가 발생한다.

추상 메소드 자식 클래스에서 오버라이딩 안한 경우 - 컴파일 오류

package sound;

public class Bird extends AbsAnimal { // 컴파일 오류
//    @Override
//    public String sound() {
//        return "짹짹";
//    }
}

public String sound() {...} 주석 처리로 컴파일 오류가 발생하였다.


추상 메소드 사용으로 해결된 문제

이전에 상속의 문제를 추상 클래스와 추상 메소드 사용으로 남은 문제점을 해결하였다.

  • Animal 클래스를 생성할 가능성
  • Animal 클래스 상속자가 메소드 오버라이딩 누락할 가능성