Java/객체지향

[객체지향][자바의 정석] - 7.6 추상클래스(Abstract class)

얄루몬 2021. 11. 28. 16:08

추상클래스(Abstract class)

 

1. 추상클래스란?

- 클래스가 설계도라면 추상클래스는 '미완성 설계도'

- 추상 메서드(미완성 메서드)를 포함하고 있는 클래스

    - 추상메서드란? 선언부만 있고 구현부(몸통, body)가 없는 메서드

- 일반 메서드가 추상 메서드를 호출할 수 있다.(호출할 때 필요한 건 선언부)

- 완성된 설계도가 아니므로 인스턴스를 생성할 수 없다.

- 다른 클래스를 작성하는 데 도움을 줄 목적으로 작성된다.

 

추상메서드란 무엇인지 아래 코드를 통해 살펴보자
package javajungsuk;

abstract class Player{
	abstract void play(int pos); //추상 메서드 몸통이 없는 미완성 메서드
	abstract void stop(); //추상 메서드
	//Player p = new Player(); 추상 클래스의 인스턴스 생성 불가

}

//추상클래스를 상속해서 추상 메서드를 완성해야 인스턴스 생성이 가능
class AudioPlayer extends Player{
	void play(int pos) {/*내용 생략*/} //추상메서드를 구현한 부분 -> 몸통 만들어주는 것
	void stop() {/*내용 생략*/} //추상메서드를 구현 -> 몸통 만들어주는 것
	
	AudioPlayer ap = new AudioPlayer(); // OK
}

이때 Player는 추상클래스지만 AudioPlayer은 완성된 설계도로 추상클래스가 아님. 

 

추상 메서드 호출 가능한 경우를 아래 코드를 통해서 살펴보도록 하자
package javajungsuk;

abstract class Player{
	
	//iv(인스턴스변수)
	boolean pause; //변수들
	int currentPos;
	
	//생성자
	Player(){ //추상클래스도 생성자가 있어야 한다. 
		pause = false;
		currentPos = 0;
	}
	
	
	//추상 메서드
	abstract void play(int pos);
	abstract void stop(); //몸통이({}) 없어야 한다. 
	
	
	//인스턴스 메서드
	void play() {
		play(currentPos); //추상메서드를 사용할 수 있다.(호출 선언부만 알면 호출가능)
        //나중에 상속을 통해서 자손이 구현하면 호출 가능
	}
}

 

 

아래의 실습코드를 통해서 추상클래스를 알아보자.
package javajungsuk;

abstract class Player{
	abstract void play(int pos);
	abstract void stop();
}


//추상클래스는 상속을 통해 완성해야 객체생성이 가능하기 때문에 상속을 통해서 구현을 먼저 해준다
class AudioPlayer extends Player{
	void play(int pos) {
		System.out.println(pos+"위치부터 play합니다.");
	}
	void stop() {System.out.println("재생을 멈춥니다.");}
}

public class PlayerTest {
	public static void main(String[] args) {
		//AudioPlayer ap = new AudioPlayer();
	
		Player ap = new AudioPlayer(); //다형성 실제 가리키고 있는 것은 AudioPlayer
        //실제 호출되는 건 AudioPlayer의 메소드이다. 
		ap.play(100);
		ap.stop();
	}
}

 

객체 생성이 불가한 경우 Feat. 조상의 추상 메서드를 자손이 모두 구현하지 않았을 경우
package javajungsuk;

abstract class Player{
	abstract void play(int pos);
	abstract void stop();
}


//추상클래스는 상속을 통해 완성해야 객체생성이 가능하기 때문에 상속을 통해서 구현을 먼저 해준다
abstract class AudioPlayer extends Player{
	void play(int pos) {
		System.out.println(pos+"위치부터 play합니다.");
	}
	

public class PlayerTest2 {
	public static void main(String[] args) {
		
		/*위의 구현부가 완전하게 구현하지 않아서 - > 조상 클래스 안에 있는 메소드를 모두 구현하지 않음
		 아래 객체 생성이 불가함 이렇게 사용하면 에러가 난다.	*/		
		Player ap = new AudioPlayer(); //다형성 실제 가리키고 있는 것은 AudioPlayer
		ap.play(100);
		ap.stop();
	}
}

 

 

2. 추상메서드란?

- 선언부만 있고 구현부(몸통, body)가 없는 메서드 

- 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우에 사용한다.

- 추상클래스를 상속받는 자손클래스에서 추상메서드의 구현부를 완성해야 한다.

 

추상메서드를 2개 물려받았는데 1개만 구현할 경우 abstract를 붙여서 사용해야 한다.
왜냐하면 조상에서 상속받은 메소드를 모두 구현하지 않아 미완성 상태이기 때문이다. 
package javajungsuk;

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{
//상속을 받아서 1개만 구현한 경우여서 abstract를 붙여주어야 한다. 
	void play(int pos) {/*내용 생략*/} //추상메서드 구현
}

 

3. 추상클래스의 작성

- 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나 기존 클래스의 공통 부분을 뽑아서 추상클래스를 만든다.  

package javaStandard;

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("Marine[x="+ x +",y= "+ y +"]");
	}
	void changeMode() {}
}
class Dropship extends Unit{ //
	void move(int x, int y) {
		System.out.println("Marine[x="+ x +",y= "+ y +"]");
	}
	void stimPack() {}
}
package javaStandard;

public class EX7_10_2 {
	public static void main(String[] args) {
		
		//Unit 타입의 참조변수 group
		Unit2[] group = new Unit2[3];
		group[0] = new Marine2();
		group[1] = new Tank2();
		group[2] = new Dropship2();
		
		
		group[0].move(100,200);
		group[1].move(100,200);
		group[2].move(100,200);
	}
}

abstract class Unit2{
	int x,y;
	abstract void move(int x, int y);
	void stop() {/*현재 위치에 정지*/}
}

class Marine2 extends Unit2{ //보병
	void move(int x, int y) {
		System.out.println("Marine[x="+ x +",y= "+ y +"]");
	}
	void stimPack() {}
}
class Tank2 extends Unit2{ //탱크
	void move(int x, int y) {
		System.out.println("Tank[x="+ x +",y= "+ y +"]");
	}
	void changeMode() {}
}
class Dropship2 extends Unit2{ //
	void move(int x, int y) {
		System.out.println("Dropship[x="+ x +",y= "+ y +"]");
	}
	void stimPack() {}
}

// 위의 코드와 아래 코드는 같다. 간결하게 바꾸는 것의 차이.

 

 

4. 추상화가 필요한 이유 

 

5. 추상화 VS 구체화

추상화된 코드는 구체화된 코드보다 유연하다. ~> 변경에 유리하다

GregotianCalendar cal = new GregotianCalendar();//구체적


Calendar cal = Calendar.getInstance(); //추상적(추상클래스)