추상 클래스(abstract class)
- 미완성 설게도. 미완성 메서드(추상메서드)를 갖고 있는 클래스
- 추상 클래스 자체로는 클래스로서의 역할을 다 못하지만, 새로운 클래스를 작성하는 데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 갖는다.
- 추상 클래스 -> iv(인스턴스 변수), 생성자, im(인스턴스 메서드), 추상 메서드
추상클래스의 구조
abstract class 클래스이름 {
....
}
abstract class Player { // 추상클래스(미완성클래스)
abstract void play(int pos); // 추상메서드
abstract void stop(); // 추상메서드
}
-> 다른 클래스 작성에 도움을 주기 위한 것이기 때문에 인스턴스 생성 불가하다!
Player p = new Player(); // 에러. 추상클래스의 인스턴스 생성 불가
-> 상속을 통해 추상 메서드를 완성해야 인스턴스 생성가능
class AudioPlayer extends player {
void player(int pos) { /*내용*/ } // 추상메서드 구현(몸통생성)
void stop() { /*내용*/ } // 추상메서드 구현(몸통생성)
}
AudioPlayer ap = new AudioPlayer(); // ok!
//Player ap = new AudioPlayer( ); 가능 왜? 다형성!!
AudioPlayer는 추상메서드가 아닌 완성된 설계도 이기 때문에 abstract이 없어도 됨!
추상 메서드(abstract method)
- 미완성 메서드(추상메서드) -> 구현부(몸통{ })가 없는 메서드(선언부만 작성)
- 상속받아서 자손클래스가 추상 메서드를 완성했지만 상속받은 메서드를 다 구현하는 게 아니라 일부만 구현한다면 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) { } // 추상메서드를 구현
}
-> 마지막 코드는 추상메서드를 구현하긴 했지만 play메서드만 구현했고,
stop메서드는 구현하지 않아서 여전히 추상클래스! 그래서 앞에 abstract이 붙는다.
추상 클래스의 작성과 장점
- 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나 기존클래스의 공통 부분을 뽑아서 추상클래스를 만든다.
상속: 자손클래스를 만드는데 조상클래스를 사용 <-> 추상화: 기존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것
구체화: 상속을 통해 클래스를 구현, 확장 <-> 추상화: 클래스 간의 공통점을 찾아내서 공통의 조상을 만드는 작업
추상클래스의 장점
1) 설계도를 쉽게 작성할 수 있다.
2) 중복 제거 가능
3) 관리가 용이(변경)
4) 추상화된 코드는 구체화된 코드보다 유연해서 변경에 유리!
예제1) Player2라는 추상클래스를 작성. 이 클래스는 VCR, Audio의 조상으로 사용될 수 있음
abstract class Player2 {
boolean pause; // 일시정지 상태를 저장하기 위한 변수
int currentPos; // 현재 Play되고 있는 위치를 저장하기 위한 변수
Player2() { // 추상클래스도 생성자가 있어야 한다.
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일 때 (play상태)에서 pause가 호출되면,
pause = true; // pause의 상태를 true로 바꾸고,
stop(); // play를 멈춘다.
}
}
}
예제2) Player2클래스를 조상으로 하는 CDPlayer 클래스 생성
class CDPlayer extends Player2 {
void play(int currentPos) {
// 조상의 추상메서드를 구현
}
void stop() {
// 조상의 추상메서드를 구현
}
//CDPlayer클레스에 추가로 정의된 멤버
int currentTrack; // 현재 재생중인 트랙
void nextTrack() {
currentTrack++;
}
void preTrack() {
if(currentTrack>1) {
currentTrack--;
}
}
}
- 조상 클래스의 추상메서드를 CDPlayer클래스의 기능에 맞게 완성해주고, CDPlayer만의 새로운 기능을 추가.
- 아무런 내용 없이 단지 괄호{ }만 있어도, 추상메서드가 아닌 일반 메서드로 간주한다.
-> abstract을 사용해 추상메서드로 정의해놓으면, 자손 클래스를 작성할 때 내용을 구현해주어야 한다는 사실을 인식하고 자신의 클래스에 알맞게 구현할 것!
예제3) 기존의 클래스로부터 공통된 부분을 뽑아내어 추상클래스 생성
abstract class Unit {
int x, y;
abstract void move(int x, int y); // 모든 자손 클래스에 있는 move 메서드
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() { /* 선택된 대상을 내린다. */ }
}
- 각 클래스의 공통부분(move)을 뽑아내서 Unit 클래스를 정의하고 이로부터 상속받도록 함
-> move 메서드가 추상메서드로 선언된 것은, 앞으로 Unit클래스를 상속받아서 작성되는 클래스는 move메서드를 자신의 클래스에 알맞게 반드시 구현해야 한다는 의미!
Unit[] group = { new Marine(), new Tank(), new Dropship() }; // group 객체 배열 생성
for(int i=0; i<group.length; i++)
group[i].move(100, 200); // Unit 배열의 모든 유닛을 좌표(100,200)의 위치로 이동시킨다.
-> 조상클래스타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있기 떄문에 조상클래스타입의 배열에 자손클래스의 인스턴스를 담을 수 있음!!!!!!(다형성)
-> Unit클래스에 move메서드가 추상메서드로 정의되어 있지만 Unit클래스 타입의 참조변수로 move메서드를 호출하는 것이 가능!!
'멀티캠퍼스 풀스택 과정 > Java의 정석' 카테고리의 다른 글
자바의 정석4-3 내부클래스와 익명클래스 (0) | 2022.01.06 |
---|---|
자바의 정석4-2 인터페이스(interface), 디폴트와 static 메서드 (0) | 2022.01.05 |
자바의 정석3-6 instanceof 연산자, 다형성의 장점(1,2) (0) | 2022.01.05 |
자바의 정석3-5 캡슐화(encapsulation)와 다형성(polymorphism) (3) | 2022.01.05 |
자바의 정석3-4 제어자(static, final, abstract, 접근제어자) (0) | 2022.01.01 |