16. 추상 클래스와 메소드


javalogo

Overview

추상 클래스와 그것을 구성하는 추상 메소드에 대해 기술하는 포스트


추상 메소드(Abstract Method)

추상 메소드(abstract method)란 선언은 되어 있으나 코드가 구현되어 있지 않은, 즉 껍데기만 있는 메소드이다.

추상 메소드를 작성하고자 하면 abstract 키워드와 함께 메소드의 타입, 이름, 매개 변수 리스트만 선언해야 한다.

다음은 추상 메소드 선언의 예이다.

public abstract String getName();
public abstract void setName(String s);

다음은 메소드의 코드가 있기 때문에 추상 메소드가 될 수 없다.

public abstract String fail() {return "Good Bye";}  // 컴파일 오류

추상 클래스의 종류

추상 클래스(abstract class)는 추상 메소드와 마찬가지로 abstract 키워드로 선언한 클래스로서 추상 클래스가 되는 경우는 다음 두 가지이다.

추상 메소드를 포함하는 클래스로서 반드시 abstract로 선언되어야 함
추상 메소드가 하나도 없지만 abstract로 선언한 클래스

두 번째 경우처럼 추상 클래스로 선언되었다고 해도 추상 메소드가 하나도 없을 수 있다.

다음은 추상 클래스를 선언한 예이다.

DObject는 추상 메소드를 가지고 있는 추상 클래스이며, Person 클래스는 추상 메소드가 없는 추상 클래스이다.

// 추상 메소드를 가진 추상 클래스
abstract class DObject {    // 추상 클래스 선언
    public DObject next;

    public DObject() { next = null;}
    abstract public void draw();    // 추상 메소드 선언
}

// 추상 메소드 없는 추상 클래스
abstract class Person {     // 추상 클래스 선언
    public String name;
    public Person(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
}

추상 클래스는 인스턴스를 생성할 수 없다

응용프로그램에서는 추상 클래스의 인스턴스를 생성할 수 없다.

추상 클래스에는 실행 코드가 없는 추상 메소드가 있을 수 있기 때문에 추상 클래스의 객체를 생성할 수 없도록 제한하였다.

그러므로 추상 클래스는 인스턴스를 생성할 목적으로 만들지 않는다.

다음은 추상 클래스의 인스턴스를 생성하려고 하였을때 컴파일 오류가 발생한 경우이다.

abstract class DObject {    // 추상 클래스 선언
    public DObject next;

    public DObject() { next = null;}
    abstract public void draw();    // 추상 메소드 선언
}

public class AbstractError {
    public static void main(String[] args) {
        DObject obj;
        obj = new DObject();    // 컴파일 오류, 추상 클래스 DObject의 인스턴스를 생성할 수 없다.
        obj.draw(); // 컴파일 오류
    }
}

위의 코드를 실행하면 오류가 발생하지만 DObject obj; 코드에서는 오류가 발생하지 않는다.

즉 추상 클래스의 레퍼런스 변수를 선언하는 것은 오류가 아니다.


추상 클래스의 상속

추상 클래스를 상속 받으면 상속받은 서브 클래스는 추상 클래스가 된다.

이는 슈퍼클래스의 추상메소드를 그대로 상속 받기 때문이다.

이 경우 서브 클래스에도 abstract를 붙여 추상 클래스임을 명시해야 컴파일 오류가 발생하지 않는다.

다음 소스와 같이 DObject가 추상 클래스이므로 이를 상속받는 Line 클래스는 추상 메소드인 draw()를 오버라이딩 하지 않으면 자동으로 추상 클래스가 된다.

그러므로 Line 클래스 앞에 abstract 키워드를 반드시 사용하여 추상 클래스임을 명시해야 한다.

abstract class DObject {    // 추상 클래스 선언
    public DObject next;

    public DObject() { next = null;}
    abstract public void draw();    // 추상 메소드 선언
}

abstract class Line extends DObject {   // draw()를 상속받아 추상 클래스가 됨
    public String toString() {return "Line";}
}

추상 클래스의 구현 및 활용

서브 클래스가 추상 클래스가 되지 않기 위해서는 추상 메소드를 모두 오버라이딩 하여 구현하여야 한다.

다음 코드는 DObject를 추상 클래스로 정의한 예이다.

DObjectdraw() 메소드는 호출하기 위해 만든것이 아니며 상속 받는 서브 클래스에서 강제로 오버라이딩하도록 지시하기 위해 만든 것이다.

Linedraw() 메소드를 오버라이딩 하였다.

/**
* code_14
* 추상 클래스가 아닌 클래스
*/
class DObject {
    public DObject next;

    public DObject() {next = null;}
    public void draw() {
        System.out.println("DObject draw");
    }
}

      \(\Downarrow\)

추상 클래스로 수정

abstract class DObject {
    public DObject next;

    public DObject() { next = null;}
    abstract public void draw();
}

추상 클래스 상속

      \(\Uparrow\)

/**
* code_15
* DObject 추상 클래스를 상속받아 구현한 클래스
*/
class Line extends DObject {
    public void draw() {
        System.out.println("Line");
    }
}

만일 main() 함수에 다음 코드가 있어도 오류가 아니다

DObject start = new Line();

DObject가 추상 클래스라고 하더라도 여전히 Line 클래스의 슈퍼 클래스이기 때문에 슈퍼클래스의 레퍼런스로 서브 클래스의 객체를 가리키는 것은 항상 합법적이다.


추상 클래스의 용도

추상 클래스를 상속받은 서브 클래스는 개발자에 따라 다양하게 구현된다.

그러나 한 가지 분명한 것은 모든 개발자들이 서브 클래스에서 추상 클래스에 선언된 추상 메소드를 모두 구현해야 한다는 사실이다.

추상 클래스를 책의 목차에 비유하면, 서브 클래스는 목차에 따라 작성된 실제 책과 같다.

책을 쓸 때도 목차를 잡아놓고 책을 쓰면 훨씬 쉽고 빠르며 방향이 흐트러지지 않는 것처럼 추상클래스를 이용하면 응용프로그램의 설계와 구현을 분리할 수 있다.

추상 클래스로 기본 방향을 잡아놓고 서브 클래스에서 구현하면 구현 작업이 쉬워진다.

또한 추상 클래스는 계층적 상속 관계를 가지는 클래스들의 구조를 만들 때 적합하다.


추상 클래스의 구현 예제

다음은 추상 클래스 Calculator를 상속받은 GoodCalc 클래스이다.

abstract class Calculator { 
    public abstract int add(int a, int b);
    public abstract int subtract(int a, int b);
    public abstract int average(int[] a);
}

public class GoodCalc extends Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    public int subtract(int a, int b) {
        return a - b;
    }
    public double average(int[] a) {
        double sum = 0;
        for (int i = 0; i < a.length; i++) {
            sum += a[i];
        }
        return sum/a.length;
    }
    public static void main(String[] args) {
        Calculator c = new GoodCalc();
        System.out.println(c.add(2,3));
        System.out.println(c.subtract(2,3));
        System.out.println(c.average(new int[] {2, 3, 4}));
    }
}
  • 실행 결과
    5
    -1
    3.0
    





© 2019. by RaP0d

Powered by aiden