추상클래스란?
클래스를 설계도에 비유한다면, 추상클래스는 미완성 설계도에 비유할 수 있다.
클래스가 미완성이라는 것은 멤버의 개수에 관계된 것이 아니라,
단지 미완성메소드(추상메소드)를 포함하고 있다는 의미이다.
미완성 설계도로 완성된 제품을 만들 수 없듯이 추상클래스로 인스턴스는 생성할 수 없다.
추상클래스는 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.
추상클래스는 키워드 'abstract'를 붙이기만 하면 된다.
이렇게 함으로써 이 클래스를 사용할 때, 클래스 선언부의 abstract를 보고 이 클래스에는
추상메소드가 있으니 상속을 통해서 구현해주어야 한다는 것을 쉽게 알 수 있을 것이다.
추상메소드란?
선언부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것이 추상메소드이다.
즉, 설계만 해 놓고 실제 수행될 내용은 작성하지 않았기 때문에 미완성 메소드인 것이다.
이렇게 미완성 상태로 남겨놓는 이유는 메소드의 내용이 상속받는 클래스에따라 달라질 수 있기 떄문에
조상클래스에서는 선언부만을 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려 주고,
실제 내용은 상속받는 클래스에서 구현하도록 비워 두는 것이다.
다음과 같이 추상메소드 앞에 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) { }
}
조상클래스의 추상메소드를 AudioPlayer클래스의 기능에 맞게 완성해주고,
AudioPlayer만의 새로운 기능들을 추가하였다.
사실 추상메소드로 구현을 하지 않고, 아무런 애용도 없이 단기 괄호{ }만 있게하여
아무 내용도 없는 메소드로 작성할 수도 있다.
어차피 자손 클래스에서 오버라이딩하여 자신의 클래스에 맞게 구현할 테니
추상메소드로 선언하는 것과 내용없는 빈 몸통만 만들어 놓는 것이나 별 차이가 없어보인다.
그래도 굳이 추상메소드로 구현하는 이유는 자손 클래스에서 추상메소드를 반드시 구현하도록 강요하기 위해서다.
추상메소드를 사용하면, 내용을 구현해주어야 한다는 사실을 인식하고 자신의 클래스에 맞게 구현할 것이기 때문이다.
이렇게 기존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것을 추상화라고 할 수 있다.
추상화를 구체화와 반대되는 의미로 이해하면 된다. 상속계층도를 따라 내려갈수록 클래스는 점점 기능이 추가되어
구체화의 정도가 심해지며, 상속계층도를 따라 올라갈수록 클래스는 추상화의 정도가 심해진다고 할 수 있다.
즉, 상속계층도를 따라 내려갈수록 세분화되며, 올라갈수록 공통요소만 남게 된다.
이번에 기존의 클래스로부터 공통된 부분을 뽑아내어 추상클래스를 만들어보자.
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 unload();
}
게임에 나오는 유닛들을 클래스로 간단히 정의해보았다.
이 유닛들은 각각 기능을 가지고 있지만 공통부분을 뽑아내어 하나의 클래스로 만들고,
이 클래스로부터 상속받도록 변경해보자.
공통된 부분은 move와 stop메소드이다.
abstract class Unit{
int x, y;
abstract void move(int x, int y);
void stop() { }
}
stop메소드는 선언부와 구현부 모두 공통적이지만, 유닛에 따라 이동하는 방법은 서로 달라서
move메소드의 실제 구현 내용이 다를 것이다.
그래도 move메소드의 선언부는 같기 때문에 추상메소드로 정의할 수 있다.
move메소드가 추상메소드로 선언된 것에는 앞으로 Unit클래스를 상속받아서 작성되는 클래스는
move메소드를 자신의 클래스에 맞게 반드시 구현해야 한다는 의미가 담겨 있는 것이기도 하다.
그리고 다음과 같이 인스턴스를 생성할 수 있다.
Unit[] group = new Unit[4];
group[0] = new Marine() ;
group[1] = new Tank() ;
group[2] = new Marine() ;
group[3] = new DropShip() ;
for(int i = 0; i<group.length; i++)
group[i].move(100, 200);
다형성에서 배웠듯이 조상 클래스타입의 참조변수로 자손 클래스의 인스턴스를 참조하는 것이 가능하기 때문에
이처럼 조상 클래스타입의 배열에 자손 클래스의 인스턴스를 담을 수 있는 것이다.
또한, Unit클래스에 move메소드가 비록 추상메소드로 정의되어 있다 하더라도 이처럼 Unit클래스 타입의 참조변수로
move메소드를 호출하는 것이 가능하다.
메소드는 참조변수의 타입에 관계없이 실제 인스턴스에 구현된 것이 호출되기 때문이다.
group[i].move(100, 200)과 같이 호출하는 것이 Unit클래스의 추상메소드인 move를 호출하는 것 같이 보이지만
실제로는 이 추상메소드가 구현된 Marine, Tank, Dropship 인스턴스의 메소드가 호출되는 것이다.
'JAVA > Basic' 카테고리의 다른 글
[JAVA-basic] 인터페이스(2) (0) | 2021.09.09 |
---|---|
[JAVA-basic] 인터페이스 (0) | 2021.08.31 |
[JAVA-basic] 다형성(2) (0) | 2021.08.24 |
[JAVA-basic] 다형성 (0) | 2021.08.24 |
[JAVA-basic] 제어자 (0) | 2021.08.20 |