[Java] 자바의 정석 : 추상 클래스와 추상 메서드
추상 클래스
: 미완성 설계도. 미완성 메서드를 갖고 있는 클래스
abstract class Player { // 추상 클래스 (미완성 클래스)
abstract void play(int pos); // 추상 메서드 (몸통{}이 없는 미완성 메서드)
abstract void stop(); // 추상 메서드
}
다른 클래스 작성에 도움을 주기 위한 것. 인스턴스 생성 불가.
Player p = new Player(); // error. 추상클래스의 인스턴스 생성 불가
상속을 통해 추상 메서드를 완성해야 인스턴스 생성가능
class AudioPlayer extends Player {
void play(int pos){/* 내용 생략 */} // 추상 메서드를 구현
void stop(){/* 내용 생략 */} // 추상 메서드를 구현
}
- 추상 클래스는 키워드 'abstract' 키워드를 붙여준다.
- 키워드를 붙임으로써 이 클래스를 사용할 때, 클래스 선언부의 abstract 를 보고 이 클래스에는 추상메서드가 있으니 상속을 통해서 구현해주어야 한다는 것을 쉽게 알릴 수 있다.
- 추상 클래스는 추상메서드를 포함하고 있다는 것을 제외하고는 일반 클래스와 전혀 다르지 않다. 추상클래스에도 생성자가 있으며, 멤버변수와 메서드도 가질 수 있다.
추상 메서드
: 선언부만 작성하고 구현부는 작성하지 않은 채로 남겨둔 것이 추상메서드이다.
즉, 설계만 해 놓고 실제 수행될 내용은 작송하지 않았기 때문에 미완성 메서드인다.
미완성 상태로 남겨 놓는 이유는?
메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에
조상 클래스에서는 선언부만을 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려주고,
실제 내용은 상속받는 클래스에서 구현하도록 비워두는 것임.
추상 메서드 작성
- 'abstract' 키워드를 앞에 분여준다.
- 구현부가 없으므로 {} 대신 문장의 끝을 알리는 ';' 을 적어준다.
주의!
- 만일 조상으로 부터 상속받은 추상메서드 중 하나라도 구현하지 않는다면, 자손 클래스 역시 추상 클래스로 지정해주어야한다.
package pkg2;
abstract class AbstractClassTest { // 추상 클래스
abstract void play(int pos);
abstract void stop();
}
class AudioPlayer1 extends AbstractClassTest{
@Override
void play(int pos) { // 추상 메서드를 구현
// 내용 생략
}
@Override
void stop() { // 추상 메서드를 구현
// 내용 생략
}
}
abstract class AbstractPlayer extends AbstractClassTest{
@Override
void play(int pos) { // 추상 메서드를 구현
// 내용 생략
}
}
위의 코드 중 AbstractPlayer 추상 클래스를 보면AbstractClassTest 추상 클래스를 상속을 받았지만
두 개의 메서드를 모두 구현하지 않고 play 메서드 하나만 구현한 것을 발견할 수 있다.
이 경우에는 나머지 하나인 stop 메서드를 구현하지 않았으므로 제어자 부분에 abstract 라는 키워드를 붙여
추상클래스로 지정을 해줘야한다.
추상 클래스의 작성
: 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나 기존클래스의 공통 부분을 뽑아서 추상 클래스를 만든다.
package pkg2;
abstract class Unit {
int x, y;
abstract void move(int x, int y);
void stop(){
// 현재 위치에 정지
};
}
class Marine2 extends Unit{
void move(int x, int y) {
}
void stimPack() {
}
}
class Tank2 extends Unit{
void move(int x, int y) {
}
void changeMode() {
}
}
class Dropship2 extends Unit{
void move(int x, int y) {
}
void load() {
}
void unload() {
}
}
- 각 클래스의 공통 부분을 뽑아서 Unit 클래스를 정의하고 이로부터 상속받도록 작성
- 이 Unit 클래스는 다른 유닛을 위한 클래스를 작성하는데 재활용될 수 있을 것이다.
- stop 메서드는 선언부와 구현부가 모두 동일하지만 이동하는 메서드인 move() 는 각각 움직이는 방식이 다르기 때문에 실제 구현내용이 다를 것이다.
- 그래도 move 메서드의 선언부는 같기 때문에 추상메서드로 정의할 수 있다.
package pkg2;
public class Ex7_10 {
public static void main(String[] args) {
Unit[] group = {new Marine(), new Tank()};
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 {
@Override
void move(int x, int y) {
System.out.println("Marine[x = " + x + ", y = " + y + "] ");
}
void stimPack() {
// 스팀팩을 사용한다.
}
}
class Tank extends Unit {
@Override
void move(int x, int y) {
System.out.println("Tank[x = " + x + ", y = " + y + "] ");
}
void changeMode() {
// 공격모드를 변환한다.
}
}
- 위의 예제에서 만일 이들 클래스 간의 공통 조상이 없었다면 이처럼 하나의 배열로 다룰 수 없을 것이다.
- Unit 클래스에 move 메서드가 비록 추상메서드로 정의되어 있다 하더라도 이처럼 Unit 클래스 타입의 참조변수로 move 메서드를 호출하는 것이 가능하다. 메서드는 참조변수의 타입에 관계 없이 실제 인스턴스에 구현된 것이 호출되기 때문이다.
group[i].move(100, 200)
과 같이 호출하는 것이 Unit 클래스의 추상 메서드를 호출하는 것 같지만 실제로는 이 추상메서드가 구현된 각각의 인스턴스의 메서드가 호출되는 것이다.
김영한의 실전 자바
섹션12 - 다형성 2
추상 클래스 1
부모 클래스는 제공하지만, 실제 생성되면 안되는 클래스를 추상 클래스라 한다.
추상 클래스는 이름 그대로 추상적인 개념을 제공하는 클래스이다.
따라서 실체인 인스턴스가 존재하지 않는다. 대신에 상속을 목적으로 사용되고, 부모 클래스 역할을 담당한다.
abstract class Animal{...}
추상 클래스는 기존 클래스와 동일하지만, new Animal(); 과 같이 인스턴스를 직접 생성하지 못한다는 제약이 추가된 것이다.
추상 메서드
package poly.ex3;
public abstract class AbstractAnimal {
public abstract void sound();
}
추상 메서드는 메서드 바디가 없다.
중요한 포인트
추상 클래스는 제약이 추가된 클래스일 뿐이다.
객체 생성이 안되고,
추상 메서드가 존재한다면, 상속 받는 클래스가 추상 메서드를 반드시 구현 해야한다는 제약
메모리 구조와 실행 결과는 모두 동일하다.
추상 클래스 2
순수 추상 클래스 : 모든 메서드가 추상 메서드인 추상 클래스
단지, 다형성을 위한 부모타입으로써 껍데기 역할만 제공할 뿐이다.
1. 인스턴스를 생성할 수 없다.
2. 상속 시, 자식은 모든 메서드를 오버라이딩 해야한다.
3. 주로 다형성을 위해 사용된다.