클래스를 설계도에 비유한다면, 추상 클래스는 미완성 설계도에 비유
미완성 설계도란, 완성되지 못한 채로 남겨진 설계도를 말하며, 멤버의 개수에 관계된 것이 아니라, 미완성 메서드(추상 메서드)를 포함하고 있다는 의미
추상 클래스로 인스턴스 생성할 수 없으며, 추상 클래스는 상속을 통해서 자손 클래스에 의해서만 완성 가능
추상 클래스는 자체로는 클래스로서의 역할을 다 못하지만, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상 클래스로서 중요한 의미를 가짐
추상 클래스는 키워드 abstract
를 붙이기만 하면 됨
클래스 선언부의 키워드 abstract
를 보고 이 클래스에는 추상메서드가 있으니 상속을 통해서 구현해주어야 한다는 것을 쉽게 알 수 있을 것
abstract class 클래스이름 {
...
}
메서드는 선언부와 구현부(몸통)로 구성
선언부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것이 추상 메서드
메서드를 미완성 상태로 남겨 놓는 이유는 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에 조상 클래스에서는 선언부만을 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려주고, 실제 내용은 상속받는 클래스에서 구현하도록 비워 두는 것
추상 메서드 역시 키워드 abstract
를 앞에 붙여주고, 추상메서드는 구현부가 없으므로 괄호{ }대신 문장의 끝을 알리는;
을 적어줌
//주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다
abstract 리턴타입 메서드이름();
추상클래스로부터 상속받는 자손클래스는 오버라이딩을 통해 조상인 추상클래스의 추상메서드를 모두 구현해주어야함
만일 조상으로부터 상속받은 추상메서드 중 하나라도 구현하지 않는다면, 자손클래스 역시 추상클래스로 지정해 주어야 함
abstract class Player { //추상클래스
abstract void play(int pos); //추상메서드
abstract void stop(); //추상메서드
}
class AudioPlayer extends Player {
void play(int pos){ /*내용 생략*/ } //추상메서드를 구현
void stop() { /*내용 생략*/ } //추상메서드를 구현
}
abstract class AbstractPlayer extends Player {
void play(int pos) { /*내용 생략*/ } //추상메서드를 구현
}
메서드를 작성할 때 실제 작업내용인 구현부보다 더 중요한 부분이 선언부임
메서드를 사용하는 쪽에서 메서드가 실제로 어떻게 구현되어 있는지 몰라도 메서드의 이름과 매개변수, 리턴타입, 즉 선언부만 알고 있으면 내용이 없을 지라도 추상메서드를 사용하는 코드를 작성하는 것이 가능하며, 자손 클래스에 구현된 완성된 메서드가 호출되도록 할 수 있음
상속이 자손 클래스를 만드는데 조상 클래스를 사용하는 것이라면, 반대로 추상화는 기존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것
상속계층도를 따라 내려 갈수록 세분화되며, 올라갈수록 공통요소만 남게 됨
클래스로 정의된 유닛들을 각자 나름대로의 기능을 가지고 있지만, 공통부분을 뽑아내어 하나의 클래스로 만들고, 이 클래스로부터 상속받도록 변경해보자
class Marine { //보병
int x, y; //현재 위치
void move(int x, int y) { /*지정된 위치로 이동*/ }
void stop() { /*현재 위치에 정지*/ }
void stimPack() {/*스팀팩을 사용한다*/}
}
class Tank { //탱크
int x, y; //현재 위치
void move(int x, int y) { /*지정된 위치로 이동*/ }
void stop() { /*현재 위치에 정지*/ }
void changeMode() {/*공격모드를 변환한다*/}
}
class Dropship { //수송선
int x, y; //현재 위치
void move(int x, int y) { /*지정된 위치로 이동*/ }
void stop() { /*현재 위치에 정지*/ }
void load() { /*선택된 대상을 태운다*/ }
void unloae() {/*선택된 대상을 내린다*/}
}
abstract class Unit {
int x,y;
abstract void move(int x, int y);
void stop() { /*현재 위치에 정지*/ }
}
class Marine extends Unit { //보병
void move(int x, int y) { /*지정된 위치로 이동*/ }
void stimPack() {/*스팀팩을 사용한다*/}
}
class Tank { //탱크
void move(int x, int y) { /*지정된 위치로 이동*/ }
void changeMode() {/*공격모드를 변환한다*/}
}
class Dropship { //수송선
void move(int x, int y) { /*지정된 위치로 이동*/ }
void load() { /*선택된 대상을 태운다*/ }
void unloae() {/*선택된 대상을 내린다*/}
}
각 클래스의 공통부분을 뽑아내서 Unit클래스를 정의하고 이로부터 상속받도록 함
이 Unit클래스는 다른 유닛을 위한 클래스를 작성하는데 재활용 가능함
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Dropship();
for(int i = 0; i<group.length; i++)
group[i].move(100,200);
/*Unit배열의 모든 유닛을 좌표 (100,200)의 위치로 이동한다*/
package Java01;
public class Ex7_10 {
public static void main(String[] args) {
Unit[]group = {new Marine(), new Tank(), new Dropship()};
for(int i = 0; i<group.length; i++)
group[i].move(100,200);
}
}
abstract class Unit {
int x, y;
abstract void move(int x, int y);
void stop() {/*현재 위치에 정지*/}
}
class Marine extends Unit { //보병
void move(int x, int y) {
System.out.println("Marine[x=" + x + ",y=" + y + "]");
}
void stimPack() { /*스팀팩을 사용한다*/ }
}
class Tank extends Unit { //탱크
void move(int x, int y) {
System.out.println("Tank[x=" + x + ",y=" + y + "]");
}
void changeMode() { /*공격모드를 변환한다*/ }
}
class Dropship extends Unit { //수송선
void move(int x, int y) {
System.out.println("Dropship[x=" + x + ",y=" + y + "]");
}
void load() { /*선택된 대상을 태운다*/ }
void unload() { /*선택된 대상을 내린다*/ }
}
위의 예제는 공통조상인 Unit클래스 타입의 객체 배열을 통해서 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있다는 것을 보여주기 위한 것
조상 타입의 배열에 자손 타입의 인스턴스를 담을 수 있음
→ 만일 클래스간의 공통조상이 없었다면 하나의 배열로 다룰 수 없음
Unit클래스에 move메서드가 비록 추상메서드로 정의되어 있다하더라도 Unit클래스 타입의 참조변수로 move메서드를 호출하는 것이 가능
→ 메서드는 참조변수의 타입에 관계 없이 실제 인스턴스에 구현된 것이 호출되기 때문
group[i].move(100,200)과 같이 호출하는 것이 Unit클래스의 추상메서드인 move를 호출하는 것 같이 보이지만 실제로는 이 추상메서드가 구현된 Maringe, Tank, Dropship인스턴스의 메서드가 호출되는 것
모든 클래스의 조상인 Object클래스 타입의 배열로도 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있지만, Object클래스에는 Move메서드가 정의되어 있지 않기 때문에 move메서드를 호출하는 부분에서 에러가 발생
Object[]group = new Object[3];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Dropship();
for(int i = 0; i<group.length; i++)
group[i].move(100,200); //에러!!!Object클래스에 move메서드가 정의되어 있지 않다