JAVA 입문 - 추상 클래스
지난 글: [프로그래밍 언어/JAVA] - JAVA 입문 - 다운 캐스팅과 instanceof
추상 클래스
추상 클래스(abstract class)란 '구체적이지 않은 클래스'이다. 내가 그간 공부하며 만들었던 클래스는 모두 구상, 구현 그리고 구체 클래스라 불리는 concrete class였다.
추상 클래스 문법
추상 클래스는 항상 추상 메서드를 포함한다. 추상 메서드는 구현 코드가 없다. 함수의 코드가 없다는 것은 함수 몸체(bo dy)가 없다는 것이다.
int add(int x, int y) {
return x + y;
}
중괄호로 감싼 부분을 함수의 구현부(implementation)라 한다. 이 부분이 없는 함수는 추상 함수(abstact function)이고 JAVA에서는 추상 메서드(abstract method)라 한다. 추상 메서드는 다음과 같이 선언만 하며 abstract 예약어를 사용한다.
abstract int add(int x, int y);
참고로 아래와 같은 메서드는 추상 메서드가 아니다. 이유는 중괄호 안에 코드가 없더라도 중괄호를 사용했다면 메서드를 구현한 것이다.
int add(int x, int y) { }
한마디로 JAVA에서 추상 메서드란, abstract 예약어를 사용하여 선언만 하는 메서드인 것이다.
메서드 선언의 의미
함수의 선언부 즉 반환 값, 함수 이름, 매개 변수를 정의한다는 것은 곧 함수의 역할이 무엇인지, 어떻게 구현해야 하는지를 정의한다는 의미다. 우리가 메서드의 선언부(declatration)만 봐도 이 메서드가 어떤 일을 하는 메서드인지 알 수 있게 메서드의 선언부를 작성해야 한다. 따라서 함수 몸체를 구현하는 것보다 중요한 것은 함수 선언부를 작성하는 것이다. 메서드를 선언한다는 것은 메서드가 해야 할 일을 명시해 두는 것이다.
추상 클래스 구현하기
구체적으로 추상 클래스의 구현 과정을 코드로 작성해보기 전에 클래스 간의 관계를 살펴볼 수 있는 클래스 다이어그램을 그려보자.
클래스 다이어 그램 맨 위에는 클래스 이름을 쓴다. 그 아래칸에 변수와 메서드 이름을 쓴다. 추상 클래스와 추상 메서드는 기울임꼴로 표시한다. Computer 클래스는 추상 클래스다. 컴퓨터 종류에는 데스크탑과 노트북이 있다. 노트북의 종류에는 내 노트북이 있다. Computer 클래스는 추상 클래스이고 이를 상속받은 두 클래스 중 DeskTop 클래스는 일반 클래스이고 NoteBook 클래스는 추상 클래스이다. 마지막으로 NoteBook 클래스를 상속받은 MyNoteBook 클래스도 일반 클래스이다. Computer 클래스가 제공하는 메서드 중 두 개(display( ), typing( ))는 기울임꼴로 표기했다. 이는 추상 메서드임을 나타낸다. 나머지 turnOn( )과 turnOff( )는 구현 코드가 있는 메서드이다.
위의 다이어그램을 프로그램으로 구현한 후 각 클래스 간의 관계를 이해해보자.
사진 처럼 추상 클래스를 구현할 때는 public 뒤에 abstract를 붙여 추상 클래스로 선언하고, 추상 메서드도 마찬가지로 public 뒤에 abstract를 붙여 추상 메서드로 선언해야한다. 또, 추상 메서드를 사용하려면 클래스는 꼭 추상 클래스여야 한다. Computer 클래스를 이렇게 구현하는 것은 'Computer를 상속받는 클래스 중 turnOn( )과 turnOff( ) 구현 코드는 공통이지만 display( )와 typing( )은 하위 클래스에 따라 구현이 달라질 수 있다. 하여 Computer에서는 구현하지 않고, 이 두 메서드 구현에 대한 책임을 상속받는 클래스에 위임한다'라는 뜻이다. 따라서 Computer 클래스의 추상 메서드는 추상 클래스를 상속 받은 DeskTop과 NoteBook에서 실제로 구현하게 된다. 이 클래스의 상위 클래스에서는 하위 클래스도 공통으로 사용할 메서드를 구현하고, 하위 클래스마다 다르게 구현할 메서드는 추상 메서드로 선언해 두는 것이다.
이제 DeskTop 클래스를 만들어 보자.
Computer를 상속받은 DeskTop 클래스에 오류가 났다. 이유는 상위 클래스인 Computer가 추상 클래스여서인데, 추상 클래스를 상속받은 클래스는 추상 클래스가 가진 메서드를 상속받는다. 따라서 상속받은 클래스는 추상 메서드를 포함하게 되는 것이다. 하여 추상 메서드를 모두 구현하든지 DeskTop도 추상 클래스로 만들든지 하나를 해야한다. 즉 추상 클래스를 상속받은 하위 클래스는 구현되지 않은 추상 메서드를 모두 구현해야 구체적은 클래스가 될 수 있다.
위의 Add unimplemented methods를 클릭하면 다음과 같이 코드가 생성된다.
주석 부분을 삭제하고 아래처럼 몸체 코드를 작성한다.
마찬가지로 NoteBook 클래스도 구현한다.
NoteBook 클래스에서는 상속받은 추상 메서드를 모두 구현하지 않고 display( ) 하나만 구현했다. 이러면 NoteBook은 추상 메서드를 하나 가지고 있기에 추상 클래스가 된다. 이러면 NoteBook을 상속받은 MyNoteBook 클래스는 다음과 같이 구현할 수 있다.
MyNoteBook은 모든 추상메서드가 구현된 클래스이므로 abstract 예약어를 사용하지 않는다.
만약 문법상으로 모든 메서드를 구현하고 클래스를 abstract로 선언한다면 그도 추상 클래스이다. 하지만 이는 생성해서 사용할 수 없고 상속만을 위한 클래스여서 new 예약어로 인스턴스를 생성할 수 없다.
추상 클래스를 만드는 이유
추상 클래스를 정의하고 구현하는 것을 배웠다. 그렇다면 이 추상 클래스는 어디에 사용하기 위해 만드는 걸까? 우선 위에서 만든 클래스들을 바탕으로 프로그램을 실행할 수 있는 테스트 프로그램을 작성해보자.
Computer 클래스형으로 인스턴스 4개를 생성했는데, Computer와 NoteBook에서 오류가 났다. 오류 메시지를 확인해 보면 Computer 클래스와 NoteBook 클래스를 인스턴스로 생성할 수 없다고 한다.
추상 클래스는 인스턴스로 생성할 수 없다.
추상 클래스는 모든 메서드가 구현되지 않았으므로 인스턴스로 생성할 수 없다. 메서드가 호출되었을 때 구현된 코드가 없으니 수행할 수 있는 내용이 없고, 그렇기에 인스턴스로 만들 수 없다. 하지만 ComputerTest처럼 DeskTop, NoteBook, MyNoteBook 클래스는 상위 클래스인 Computer를 상속받았으므로 Computer형으로 선언하여 사용할 수 있다.
추상 클래스에서 구현하는 메서드
생성할 수 없는 추상 클래스는 상속을 위해 만든 클래스이다. 추상 클래스에서 구현하는 메서드는 하위 클래스에서도 사용할, 구현 내용을 공유할 메서드를 구현한다. 만약 실제 하위 클래스에서 내용을 각각 다르게 구현해야 한다면, 구현 내용을 추상 메서드로 남겨 두고 하위 클래스에 구현을 위임하는 것이다. 앞서 만든 Computer 클래스의 turnOn( )과 turnOff( )의 구현은 하위 클래스에서 공유할 수 있지만, display( )와 typing( )의 구현 내용은 DeskTop인지 NoteBook인지에 따라 달라지므로 Computer에서 구현하지 않은 것이다. 참고로 추상 클래스는 많은 프레임워크에서 사용하고 있는 구현 방식이라고 하니 잘 숙지해두자.
참고 서적: 자바 프로그래밍 입문 - 박은종