일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 접근제어자
- Interface
- 예외
- 커넥션 풀
- 내장 객체 영역
- JSP
- java
- array
- 에러
- ToString
- I/O
- 객체
- 메서드
- File입출력
- StringBuffer
- DB
- StringBuffer클래스
- 상속
- Connection
- 객체지향
- try-catch
- 배열
- for문
- 다형성
- 람다식
- 입출력
- 예외처리
- 제어자
- 변수
- 인터페이스
- Today
- Total
ksouth9
객체지향언어(16) - 추상클래스 본문
추상클래스
하나 이상의 추상 메소드를 포함하는 클래스
추상클래스는 미완성 설계도에 비유할 수 있다.
클래스가 미완성이라는 것은 멤버의 개수에 관계된 것이 아니라, 단지 미완성 메서드(추상메서드)를 포함하고 있다는 의미이다.
- 추상클래스는 인스턴스를 생성할 수 없다.
- 추상클래스는 상속을 통해서 자손클래스에 의해서만 완성될 수 있다.
- 추상클래스는 추상메서드를 포함하고 있다는 것을 제외하고는 일반클래스와 전혀 다르지 않다.
- 추상클래스에도 생성자가 있으며, 멤버변수와 메서드도 가질 수 있다.
생성방법
abstract class 클래스이름
abstract class 클래스이름 {
...
}
추상메서드
선언부만 작성하고 구현부는 작성하지 않은 채로 남겨 둔 것이 추상메서드이다.
즉, 설계만 해 놓고 실제 수행될 내용은 작성하지 않았기 때문에 미완성 메서드인 것이다. 메서드를 이와 같이 미완성 상태로 남겨 놓는 이유는 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에 조상 클래스에서는 선언부만 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려 주고, 실제 내용은 상속받는 클래스에서 구현하도록 비워 두는 것이다.
생성방법
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) { /* 내용 생략 */ } //추상메서드를 구현
}
추상클래스와 오버라이딩에 차이점이라면 추상클래스는 자손클래스가 조상클래스에 있는 추상메서드를 반드시 구현해야한다.
반대로 오버라이딩은 조상클래스의 메서드를 구현해도 되고 안해도 문제 없다.
추상클래스 작성
상속이 자손 클래스를 만드는데 조상 클래스를 사용하는 것이라면, 이와 반대로 추상화는 기존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것이라고 할 수 있다. 추상화를 구체화와 반대되는 의미로 이해하면 보다 쉽게 이해할 수 있다. 상속계층도를 따라 내려갈수록 클래스는 점점 기능이 추가되어 구체화의 정도가 심해지며, 상속계층도를 따라 올라갈수록 클래스는 추상화의 정도가 심해진다고 할 수 있다. 즉, 상속계층도를 따라 내려 갈수록 세분화되며, 올라갈수록 공통요소만 남게 된다.
추상화 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
구체화 상속을 통해 클래스를 구현, 확장하는 작업
추상클래스 사용방법
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일 때(play상태)에서 pause가 호출되면,
pause = true; //pause의 상태를 true로 바꾸고,
stop(); //play를 멈춘다.
}
}
}
public class CDPlayer extends Player {
@Override
void play(int pos) {
/* 조상의 추상메서드를 구현. 내용 생략 */
}
@Override
void stop() {
/* 조상의 추상메서드를 구현. 내용 생략 */
}
//CDPlayer 클래스에 추가로 정의된 멤버
int currentTrack; //현재 재생 중인 트랙
void nextTrack() {
currentTrack++;
//...
}
void preTrack() {
if(currentTrack > 1) {
currentTrack--;
//...
}
}
}
조상 클래스의 추상메서드를 CDPlayer클래스의 기능에 맞게 완성해주고, CDPlayer만의 새로운 기능들을 추가했다.
기존의 클래스로부터 공통괸 부분을 뽑아내어 추상클래스를 만드는 예시
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 [] 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클래스 타입의 참조변수 배열을 통해서 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있다는 것을 보여준다.
조상 클래스타입의 참조변수로 자손 클래스의 인스턴스를 참조하는 것이 가능하기 때문에 이처럼 조상 클래스타입의 배열에 자손 클래스의 인스턴스를 담을 수 있는 것이다.
group[i].move(100,200)과 같이 호출하는 것이 Unit클래스의 추상매스드인 move를 호출하는 것 같이 보이지만 실제로는 이 추상메드가 구현된 Marine, Tank, Dropship인스턴스의 메서드가 호출되는 것이다.
메서드는 참조변수의 타입에 관계없이 실제 인스턴스에 구현된 것이 호출된다.
'Java' 카테고리의 다른 글
객체지향언어(16) - 인터페이스(interface)(2) (0) | 2022.03.16 |
---|---|
객체지향언어(16) - 인터페이스(interface)(1) (0) | 2022.03.16 |
List 컬렉션 (0) | 2022.03.16 |
객체지향언어(15) - 다형성(2) (0) | 2022.03.11 |
객체지향언어(15) - 다형성(1) (4) | 2022.03.10 |