추상클래스란?
- 클래스를 설계도에 비유한다면, 추상클래스는 미완성 설계도에 비유할 수 있습니다.
- 추상클래스가 미완성이라는 것은 맴버의 개수에 관계된 것이 아닌, 단지 미완성 메서드( 추상메서드 )를 포함하고 있다는 의미입니다. ( 추상메서드가 없더라도 인스터스 생성을 제한하기 위해 추상클래스로 만들 수 있습니다. )
- 추상클래스는 미완성 클래스이므로 인스턴스를 생성할 수 없으며, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 갖습니다.
- 추상클래스는 클래스 선언부에 키워드 'abstract'를 붙여서 선언할 수 있습니다.
abstract class 클래스이름{
... // 추상메서드, 생성자, 멤버변수, 메서드 등
// 추상메서드의 유무를 제외하면 일반 클래스와 동일한 맴버를 가질 수 있음.
}
추상메서드 ( abstract method )
- 메서드는 선언부와 구현부로 구성되어 있습니다. 메서드의 선언부만 작성하고 구현부는 작성하지 않는 채로 남겨 둔 것이 바로 추상메서드입니다.
- 추상메서드의 실제 구현은 상속받는 클래스에 따라 달라질 수 있으며, 조상클래스는 메서드 선언부만을 작성하고 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려 주어 자손클래스가 상황에 맞게 구현하도록 해주어야 합니다.
// 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명합니다.
abstract 리턴타입 메서드이름();
- 추상클래스를 상속받는 자손클래스는 오버라이딩을 통해 추상메서드를 모두 구현해주어야 합니다.
- 모든 추상메서드를 구현하지 않으면 자손클래스 역시 추상클래스로 지정해 주어야 합니다.
추상클래스 작성 - 추상화와 구체화
- 상속이 자손클래스를 만드는데 조상클래스를 사용하는 것이라면, 이와 반대로 추상화는 기존의 클래스의 공통부분을 뽑아내서 조상클래스로 만드는 것이라고 할 수 있습니다.
- 추상화를 구체화와 반대되는 의미로 이해할 수 있으며, 상속계층을 따라 내려갈수록 클래스는 점점 기능이 추가되어 구체화의 정도가 심해지며, 상속계층도를 따라 올라갈수록 추상화의 정도가 심해진다고 할 수 있습니다.
추상화 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
구체화 상속을 통해 클래스를 구현, 확장하는 작업
추상클래스 작성하기 ( 1 )
abstract class Player {
boolean pause; // 일시정지 상태를 저장하기 위한 변수
int currentPos; // 현재 Play되고 있는 위치를 저장하기 위한 변수
Player() { // 추상클래스도 생성자가 있어야 합니다.
pause = false;
currentPos = 0;
}
/* 지정된 위치(pos)에서 재생을 시작하는 기능이 수행하도록 작성하여야 합니다. */
abstract void play(int pos); // 추상 메서드
/* 재생을 즉시 멈추는 기능을 수행하도록 작성되어야 합니다. */
abstract void stop(); // 추상 메서드
void play() {
play(currentPos); // 추상메서드를 사용할 수 있습니다.
}
void pause() {
if (pause) { // 정지 상태에서 호출되면
pause = false; // pause를 false로 바꾸고,
play(currentPos); // 현재의 위치에서 play
}
else {
pause = true; // puase 상태를 true로 바꾸고,
stop(); // play를 멈춥니다.
}
}
}
class CDPlayer extends Player {
@Override
void play(int currentPos) {
/* 조상의 추상메서드 구현 */
}
@Override
void stop() {
/* 조상의 추상메서드 구현 */
}
// CDPlayer 클래스에 새로 추가된 멤버
int currentTrack; // 현재 트렉
void nextTrack() {
currentTrack++;
}
}
- 조상클래스의 추상메서드를 CDPlayer 클래스의 기능에 맞게 완성해주고, CDPlayer만의 새로운 기능들을 추가한 예시입니다.
- 사실 다음과 같이 추상메서드를 아무 내용도 일반메서드로 작성하고, 자손클래스에서 오버라이딩하여 자신의 클래스에 맞게 구현할 수도 있습니다.
class Player{
...
void play(int pos){} // 아무런 내용도 없는 빈 일반메서드
void stop(){} // 아무런 내용도 없는 빈 일반메서드
...
}
- 굳이 abstract를 붙여서 추상메서드로 선언하는 이유는 자손클래스에서 추상메서드를 반드시 구현하도록 강요하기 위해서입니다.
- 위와 같이 빈 몸통만 가지도록 정의되어 있는 경우, 자손클래스에서 이 메서드들이 온전히 구현된 것으로 인식하고 오버라이딩을 통해 자신의 클래스에 맞게 구현하지 않을 수도 있기 때문에 이를 방지하고자 추상메서드를 사용하는 것입니다.
추상클래스 작성하기 ( 2 )
- 기존의 클래스로부터 공통된 부분을 뽑아서 추상클래스를 만들어 보겠습니다.
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 unLoad(){ /* 선택된 대상을 내리는 구현부분 ( 생략 ) */}
}
- 위 클래스들은 각자 나름대로의 기능을 가지고 있지만 공통부분을 뽑아내어 하나의 추상클래스로 만들고 이 클래스로부터 상속받도록 변경할 수 있습니다.
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 extends Unit{ // 탱크
void move(int x, int y){ /* 추상 클래스를 구현 ( 생략 ) */}
void changeMode(){ /* 탱크의 공격모드를 변환하는 구현부분 ( 생략 ) */}
}
Class DropShip extends Unit{ // 수송선
void move(int x, int y){ /* 추상 클래스를 구현 ( 생략 ) */}
void load(){ /* 선택된 대상을 태우는 구현부분 ( 생략 ) */}
void unLoad(){ /* 선택된 대상을 내리는 구현부분 ( 생략 ) */}
}
- 각 클래스의 공통부분을 뽑아내서 Unit 클래스를 정의하였고 이로부터 상속하도록 하였습니다.
- 이렇게 함으로써 Unit 클래스는 새로운 유닛을 위한 클래스를 작성하는 데 재활용될 수 있을 것입니다.
- 또한, 각 자손클래스들의 공통 조상인 Unit 클래스 타입의 참조변수 배열을 통해서 다음과 같이 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있을 것입니다.
Unit[] group = new Unit[4];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Marine();
group[3] = new DropShip();
for(Unit u : group){
u.move(100, 200); // Unit 배열의 모든 유닛을 좌표 ( 100, 200 )의 위치로 이동한다.
}
- Unit 클래스에 move 메서드가 추상 메서드로 정의되어 있더라도, 메서드는 참조변수의 타입에 관계없이 실제 인스턴스에 구현된 것이 호출되기 때문에 Unit 클래스 타입의 참조변수로 move 메서드를 호출하는 것이 가능합니다. ( 각 인스턴스마다 Marine, Tank, DropShip에서 구체화한 move 메서드가 실행됩니다. )
'Java' 카테고리의 다른 글
[ JAVA의 정석 ] 지네릭스 ( Generics ) (0) | 2023.08.13 |
---|---|
[ Java 의 정석 ] 내부 클래스 ( Inner class ) (0) | 2023.08.03 |
[ Java의 정석 ] 인터페이스 ( interface ) (0) | 2023.08.01 |
[ Java의 정석 ] 다형성 ( polymorphism ) (0) | 2023.07.29 |
[ Java의 정석 ] 제어자 ( modifier ) (0) | 2023.07.27 |