Java/정석

추상 클래스

hs_developer 2022. 6. 2. 17:01

추상 클래스

 

미완성 메서드(추상 메서드)를 포함한 클래스를 뜻한다.

 

미완성 설계도로 완성된 작품을 만들 수 없 듯이 추상 클래스로 인스턴스를 생성할 수 없다. 추상 클래스는 상속을 통해서 child 클래스에 의해서만 완성된다.

 

추상 클래스 자체로는 클래스로서 역할을 다 못하지만, 새로운 클래스를 작성하는데 바탕이 되는 parent 클래스로서 중요한 의미를 갖는다. 새 클래스를 작성할 때 아무 것도 없는 상태에서 시작하는 것보다는 완전하지는 못 하더라도 어느 정도 틀을 갖춘 상태에서 시작하는 게 나으니.

 

TV도 여러 종류 모델이 있지만 설계도는 거의 비슷하다. 이 들의 공통 부분만을 그린 미완성 설계도를 만들어 놓고, 이 걸 이용해 각각의 설계도를 완성하는 게 더 효율적이다.

 

 

추상 클래스 사용법

abstract class 클래스 이름
{
	...
}

 

추상 클래스는 추상 메서드를 포함하고 있다는 것만 빼면 일반 클래스와 전혀 다르지 않다. 추상 클래스에서도 생성자가 있으며, 멤버 변수와 메서드를 가진다.

 

 

 

추상 메서드

메서드는 선언부와 구현부로 구성되어 있다. 추상 클래스는 선언부만 작성하고 구현부는 미작성으로 남겨놓는다. 설계만 해놓고 실제 수행 내용은 작성하지 않았기 때문에 미완성 메서드라 한다.

 

메서드를 미완성 상태로 남기는 이유는 메서드의 내용이 상속 받는 클래스에 따라 달라질 수 있기 때문이다. 그래서 추상 클래스를 상속 받는 child  클래스는 parent 추상 메서드를 적절히 구현해야 한다.

 

추상 메서드도 키워드 abstract을 앞에 붙이고, 구현부가 없으므로 괄호 {} 대신 ;을 찍는다.

 

/* 주석으로 어떤 기능인지 설명한다. */
abstract 리턴타입 메서드이름();

 

 

추상 클래스로부터 상속 받는 child 클래스는 오버라이딩을 통해 parent 추상 클래스의 추상 메서드를 모두 구현해 주어야 한다. 

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) // 추상 메서드 구현
    {
    	...
    }
}

 

 

메서드를 작성할 때 작업 내용인 구현부보다 더 중요한 부분이 선언부다. 

 

메서드 이름, 매개변수, 리턴 타입을 결정하는 건 쉽지 않기 때문에 선언부만 작성해도 메서드 절반 이상이 완성된 것이다.

 

메서드를 사용하는 쪽에서는 메서드가 실제로 어떻게 구현되어 있는지 몰라도 메서드의 이름과 매개변수, 리턴타입, 즉 선언부만 알고 있으면 되므로 내용이 없어도 추상메서드를 사용 코드 작성이 가능하며, 실제로 child 클래스에 구현된 완성된 메서드가 호출되게 할 수 있다.

 

 

 

추상 클래스 작성

 

상속이 child 클래스를 만드는데 parent 클래스를 사용하는 것이라면, 추상화는 기존의 클래스의 공통 부분을 뽑아내서 parent 클래스를 만드는 것이다.

 

 

1. CDPlayer를 child 클래스로 하는 Player 클래스

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가 true일 때 (정지상태)에서 pause가 호출되면
		{
			pause = false; // pause 상태를 false로 바꾸고
			play(currentPos); // 현재의 위치에서 play한다
		}
		else // pause가 false일 때 pause가 호출되면
		{
			pause = true; // pause의 상태를 true로 바꾸고
			stop(); // play를 멈춘다
		}
	}
	
}

 

 

Player 클래스를 parent 클래스로 하는 CDPlayer 클래스

public class CDPlayer {

	void play(int currentPos)
	{
		// parent의 추상메서드 구현..
	}
	
	void stop()
	{
		// parent의 추상메서드 구현..
	}
	
	// CDPlayer에 추가로 정의된 멤버
	int currentTrack; // 현재 재생 중인 트랙
	
	void nextTrack()
	{
		currentTrack++;
		// ...
	}
	
	void preTrack()
	{
		if(currentTrack > 1)
		{
			currentTrack--;
		}
		
		// ..
	}
}

 

 

 

기존의 클래스로부터 공통된 부분 뽑아내 추상클래스 만들기

 

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
{
	int x, y; // 현재 위치
	
	void move(int x, int y)
	{
		// 지정된 위치로 이동
	}
	
	void load()
	{
		// 선택된 대상 태운다
	}
	
	void unload()
	{
		// 선택된 대상 내린다
	}
}

 

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 배열의 모든 유닛을 좌표(100, 200)의 위치로 이동한다

 

Unit 클래스의 참조변수 배열을 통해 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있다는 걸 보여준다.

 

parent 클래스의 참조 변수로 child 클래스의 인스턴스를 참조하는 것이 가능하기 때문에 이처럼 parent 클래스 타입의 배열에 child 클래스의 인스턴스를 담을 수 있다.

 

 

 

'Java > 정석' 카테고리의 다른 글

clone  (0) 2022.06.10
인터페이스  (0) 2022.06.03
다형성  (0) 2022.06.01
오버라이딩  (0) 2022.06.01
static, instance 메소드  (0) 2022.05.29