[Java] 추상 클래스

4 분 소요

추상클래스란?

‘추상적이다’라는 말의 뜻을 생각해 보자. 추상적이라는 것은 구체적이지 않고 막연한 것을 뜻한다.
그렇다면 어떤 클래스가 추상적이다 라는 말은 무슨 뜻일까? 앞의 용어풀이를 대입해 보면 ‘구체적이지 않은 클래스’ 라는 뜻일 것이다. 추상 클래스를 영어로 하면 abstract class이고, 추상 클래스가 아닌 클래스는 concrete class 라고 한다. 우리가 지금까지 만든 클래스는 모두 concrete class이다. 추상 클래스 활용방법을 살펴보기 전에 추상 클래스 문법부터 배워보자.

추상 클래스는 항상 추상메서드 를 포함한다. 추상메서드는 구현코드가 없다. 함수의 구현 코드가 없다는 것은 함수 몸체가 없다는 뜻이다.

1
2
3
int add(int x, int y){
    return x + y;
}

중괄호 {}로 감싼 부분을 함수의 구현부(implementation)라고 하며, 이 부분이 없는 함수는 추상 함수(abstract function)이고 자바에서는 추상 메서드(abstract method) 라고 한다. 추상메서드는 다음과 같이 선언만 하며 abstract 예약어를 사용한다. 또한 {}대신 ;를 사용한다.

1
abstract int add(int x int y)

참고로 아래와 같은 메서드는 추상 메서드가 아니다.
중괄호를 사용한 것 만으로 메서드를 구현한 셈이기 때문이다.

1
int add(int x, int y){}

정리하자면, 자바에서 추상 메서드는 abstract 예약어를 사용하여 선언만 하는 메서드이다.

이러한 추상 클래스를 이해하려면 먼저 메서드 선언의 의미에 대해서 생각해보아야한다.
아래의 코드를 보자.

1
int add(int num1, int num2);

위 코드처럼 선언한 메서드를 보면 두 개의 정수를 입력받은 후 더해서 그 결과 값을 반환한다는 것을 유추할 수 있다. 즉 이 메서드의 선언부(declaration)만 봐도 어떤일을 하는 메서드인지 알수 있는 것이다. 함수의 선언부 즉 반환 값, 함수 이름, 매개변수를 정의 한다는것은 곧 함수의 역할이 무엇인지, 어떻게 구현해야 하는지를 정의한다는 뜻이다. 우리가 자바에서 사용하는 메서드 역시 마찬가지이다. 메서드를 선언한다는 것은 해야 할 일을 명시해 두는 것이다.

추상클래스 구현하기

아래의 코드를 보고, 추상클래스에 대해 알아보자

1
2
3
4
5
6
7
8
9
10
11
12
package abstractex;

public class Computer {
    public void display();
    public void typing();
    public void turnOn(){
        System.out.println("전원을 켭니다");
    }
    public void turnOff(){
        System.out.println("전원을 끕니다.");
    }
}

컴퓨터라는 클래스를 만들어주고 display와 typing, turnOn,turnOff 메서드를 선언해주었는데, 함수구현부를 써주지않은 display와 typing에서는 오류가 발생하고 있다. 이 부분이 오류가 나지 않으려면 몸체 부분을 작성하거나, 이 메서드를 추상 메서드로 선언해주어야 한다. 그럼 display와 typing 앞에 abstract 예약어를 사용해보자.

1
2
3
4
5
6
7
8
9
10
11
12
package abstractex;

public class Computer {
    public abstract void display();
    public abstract void typing();
    public void turnOn(){
        System.out.println("전원을 켭니다");
    }
    public void turnOff(){
        System.out.println("전원을 끕니다.");
    }
}

그러면 이제 오류가 없어졌지만, 이제는 class Computer에 오류가 뜬다. 왜냐하면 추상 메서드가 속한 클래스를 추상 클래스로 선언하지 않았기 때문이다. 그러니 이번에는 Computer 클래스를 추상 클래스로 바꿔서 선언해보자.

1
2
3
4
5
6
7
8
9
10
11
12
package abstractex;

public abstract class Computer {
    public abstract void display();
    public abstract void typing();
    public void turnOn(){
        System.out.println("전원을 켭니다");
    }
    public void turnOff(){
        System.out.println("전원을 끕니다.");
    }
}

이제 모든 오류가 제거되었다. Computer 클래스에 이렇게 구현한 이유는
Computer를 상속받는 클래스를 이와 같이 구현한것은 ‘Computer를 상속받는 클래스 중 turnOn과 turnOff( ) 구현 코드는 공통이다. 하지만 display()와 typing()은 하위 클래스에 따라 구현이 달라질 수 있다. 그래서 Computer에서는 구현하지 않고, 이 두 메서드 구현에 대한 책임을 상속받는 클래스에 위임한다’ 라는 의미이다.

따라서 Computer 클래스의 추상 메서드는 추상 클래스를 상속받은 DeskTop과 NoteBook에서 실제로 구현하게 된다. 이 클래스의 상위 클래스에서는 하위 클래스도 공통으로 사용할 메서드를 구현하고, 하위 클래스마다 다르게 구현할 메서드는 추상 메서드로 선언해 두는 것이다.

그러면 DeskTop 클래스를 만들어보자. 다음과 같이 DeskTop 클래스를 선언하고 Computer 클래스를 상속받는다.

1
2
3
4
package abstractex;

public class DeskTop extends Computer{
}

이렇게 Computer를 상속한 DeskTop을 만들어주면 오류가 발생한다.

추상 클래스를 상속했으므로, 위에서 추상메서드로 선언했던 display() 와 typing() 메서드의 몸체 부분을 상속받은 클래스에서 구현해주던가, 상속한 DeskTop도 추상 클래스로 선언해주던가 둘중에 하나를 해주어야 오류가 발생하지 않는다. display()와 typing() 메서드의 몸체 부분을 작성해보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package abstractex;

public class DeskTop extends Computer{

    @Override
    public void display() {
        System.out.println("DeskTop display()");
    }

    @Override
    public void typing() {
        System.out.println("Desktop typing()");
    }
}

마찬가지로 NoteBook 클래스도 구현한다.

1
2
3
4
5
6
7
8
9
10
11
package abstractex;

public abstract class NoteBook extends Computer {


    @Override
    public void display() {
        System.out.println("Notebook display");
    }

}

NoteBook 클래스는 추상 클래스로 선언해주기 위해 abstract 예약어를 사용했다.
그럼 NoteBook 클래스를 또 상속하는 MyNoteBook 클래스를 만들어보자.

1
2
3
4
5
6
7
8
package abstractex;

public class MyNoteBook extends NoteBook{
    @Override
    public void typing(){
        System.out.println("MyNoteBook typing()");
    }
}

이렇게 하면, 가장 상위클래스이자 추상클래스인 Computer에서 추상 메서드인 display와 typing이 선언되고, 그 아래의 DeskTop 클래스는 두가지 메서드를 모두 재정의 하여 추상클래스가 아닌 클래스가 되었고, NoteBook은 display 하나만 재정의 함으로써 추상클래스로 선언된걸 확인 할 수 있다. 마지막으로 MyNoteBook은 NoteBook을 상속받은 뒤, NoteBook에서 선언하지 않은 typing을 재정의 해줌으로써 구체적 클래스가 된다.

추상 클래스를 만드는 이유

추상 클래스를 어떻게 정의하고 구현하는지를 이야기 했다. 그렇다면 이런 추상 클래스는 어디에 사용하기 위해 사용하는 것일까? 앞서 만든 추상클래스를 사용하여 알아보자.

1
2
3
4
5
6
7
8
9
10
package abstractex;

public class ComputerTest {
    public static void main(String[] args){
        Computer c1 = new Computer();
        Computer c2 = new DeskTop();
        Computer c3 = new NoteBook();
        Computer c4 = new MyNoteBook();
    }
}

ComputerTest 클래스를 만들어 메인함수를 통해 인스턴스를 각각의 자료형으로 4개 선언해주었다.
추상 클래스인 Computer 와 NoteBook이 오류가 나는것을 확인 할 수 있다.

이러한 오류가 발생하는 이유는 바로 추상클래스는 인스턴스로 생성 할 수 없기 떄문이다.

추상 클래스는 상속을 하기 위해 만든 클래스로, 하위클래스에서 각각 다르게 구현해야 할 메서드가 있다면, 구현 내용 제목만 상위클래스에 남겨두고 이러한 내용을 하위클래스에서 재정의하여 사용하도록 한 것이 바로 추상 클래스이다.

추상클래스는 이러한 특징때문에 많은 프레임워크에서 사용되고 있는 구현방식인데, 앱마다 어떻게 만드는지에 따라 다르게 구현해야할 클래스인 경우, 이런식으로 추상클래스로 선언하여 사용하게되는 것이다.

카테고리:

업데이트:

댓글남기기