[Java의 정석]Chapter7.객체지향 프로그래밍2(31~34)

Hello_Wendy·2022년 5월 23일
0

자바의 정석

목록 보기
60/61
post-thumbnail

Chapter 7. 객체지향 프로그래밍 2

31. 추상 클래스(abstract class)

  • 클래스를 설계도에 비유한다면, 추상 클래스는 미완성 설계도에 비유

  • 미완성 설계도란, 완성되지 못한 채로 남겨진 설계도를 말하며, 멤버의 개수에 관계된 것이 아니라, 미완성 메서드(추상 메서드)를 포함하고 있다는 의미

  • 추상 클래스로 인스턴스 생성할 수 없으며, 추상 클래스는 상속을 통해서 자손 클래스에 의해서만 완성 가능

  • 추상 클래스는 자체로는 클래스로서의 역할을 다 못하지만, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상 클래스로서 중요한 의미를 가짐

  • 추상 클래스는 키워드 abstract를 붙이기만 하면 됨

  • 클래스 선언부의 키워드 abstract를 보고 이 클래스에는 추상메서드가 있으니 상속을 통해서 구현해주어야 한다는 것을 쉽게 알 수 있을 것

abstract class 클래스이름 {
 ...
}
  • 추상 클래스는 추상 메서드를 포함하고 있다는 것을 제외하고는 일반 클래스와 전혀 다르지 않음. 생성자와 멤버변수, 메서드도 가질 수 있음

32. 추상 메서드(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) { /*내용 생략*/ } //추상메서드를 구현
}
  • 메서드를 작성할 때 실제 작업내용인 구현부보다 더 중요한 부분이 선언부임

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

33. 추상클래스의 작성

  • 상속이 자손 클래스를 만드는데 조상 클래스를 사용하는 것이라면, 반대로 추상화는 기존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것

  • 상속계층도를 따라 내려 갈수록 세분화되며, 올라갈수록 공통요소만 남게 됨

  • 클래스로 정의된 유닛들을 각자 나름대로의 기능을 가지고 있지만, 공통부분을 뽑아내어 하나의 클래스로 만들고, 이 클래스로부터 상속받도록 변경해보자

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 unloae() {/*선택된 대상을 내린다*/}
}

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 { //탱크	
    void move(int x, int y) { /*지정된 위치로 이동*/ }   
    void changeMode() {/*공격모드를 변환한다*/}
}
class Dropship { //수송선	
    void move(int x, int y) { /*지정된 위치로 이동*/ }    
    void load() { /*선택된 대상을 태운다*/ }
    void unloae() {/*선택된 대상을 내린다*/}
}
  • 각 클래스의 공통부분을 뽑아내서 Unit클래스를 정의하고 이로부터 상속받도록 함

  • 이 Unit클래스는 다른 유닛을 위한 클래스를 작성하는데 재활용 가능함

group[0] = new Marine();
group[1] = new Tank();
group[2] = new Dropship();

for(int i = 0; i<group.length; i++)
	group[i].move(100,200); 
    /*Unit배열의 모든 유닛을 좌표 (100,200)의 위치로 이동한다*/

34. 추상클래스의 작성 예제

package Java01;

public class Ex7_10 {

	public static void main(String[] args) {
		Unit[]group = {new Marine(), new Tank(), new Dropship()};
		
		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 { //보병
	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() { /*선택된 대상을 내린다*/ }
}

  • 위의 예제는 공통조상인 Unit클래스 타입의 객체 배열을 통해서 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있다는 것을 보여주기 위한 것

  • 조상 타입의 배열에 자손 타입의 인스턴스를 담을 수 있음
    → 만일 클래스간의 공통조상이 없었다면 하나의 배열로 다룰 수 없음

  • Unit클래스에 move메서드가 비록 추상메서드로 정의되어 있다하더라도 Unit클래스 타입의 참조변수로 move메서드를 호출하는 것이 가능
    → 메서드는 참조변수의 타입에 관계 없이 실제 인스턴스에 구현된 것이 호출되기 때문

  • group[i].move(100,200)과 같이 호출하는 것이 Unit클래스의 추상메서드인 move를 호출하는 것 같이 보이지만 실제로는 이 추상메서드가 구현된 Maringe, Tank, Dropship인스턴스의 메서드가 호출되는 것

  • 모든 클래스의 조상인 Object클래스 타입의 배열로도 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있지만, Object클래스에는 Move메서드가 정의되어 있지 않기 때문에 move메서드를 호출하는 부분에서 에러가 발생

Object[]group = new Object[3];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Dropship();

for(int i = 0; i<group.length; i++)
	group[i].move(100,200); //에러!!!Object클래스에 move메서드가 정의되어 있지 않다
profile
안녕 나의 새로운 세상

0개의 댓글