관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch7 - 31 ~ 34[추상 클래스, 추상 메서드, 추상클래스의작성1~2] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch7 - 31 ~ 34[추상 클래스, 추상 메서드, 추상클래스의작성1~2]

소소한나구리 2023. 11. 28. 17:32

1) 추상클래스

  • 미완성 설계도(미완성 메서드를 갖고 있는 클래스)
  • 미완성 메서드는 선언부만 가지고 있으며 구현부가 없음
  • abstract 리턴타입 메서드이름(); 으로 정의함
  • abstract를 붙힘으로 인하여 '나중에 완성해야 하는구나' 라는 인식을 주도록 할 수 있음 (필수 요소에 대한 강제성 부여)
  • 클래스와 메서드 앞에 abstract 붙혀야 에러가 안남
  • 다른 클래스 작성에 도움을 주기 위한 것으로 그 자체로는 인스턴스 생성이 불가능 함
  • 상속을 통해 추상 메서드를 완성해야 인스턴스 생성 가능
// abstract를 안붙히면 에러남(메서드, 클래스 전부)
abstract class Player {          // 추상클래스(미완성클래스)
    abstract void play(int pos); // 추상메서드(블럭{}이 없는 미완성 메서드)
    abstract void stop();        // 추상메서드
}

class AudioPlayer extends Player {	// 추상클래스 상속
	void play(int pos) { /* 내용생략 */  }	// 추상메서드 구현
	void stop() { /* 내용생략 */ }	// 추상메서드 구현
}

public class Test {
    public static void main(String[] args) {
        // Player p = new Player();	        // 에러, 추상클래스의 인스턴스 생성 불가
        AudioPlayer ap = new AudioPlayer();	// 객체 생성 가능
        Player p = new AudioPlayer();	    // 조상 타입의 참조변수로도 가능(다형성)
    }
}
  • 메서드의 기능이 애플리케이션에 꼭 필요하지만 자손클래스마다 다르게 구현 될 것으로 예상되는 경우에 사용
  • 추상클래스를 상속받고 추상 메서드를 전부 구현하지 않은채 abstract를 제외하면 에러 발생
  • 즉, 상속받은 메서드의 일부만 구현하게되면 상속받은 클래스도 추상클래스가 됨
abstract class AbstractPlayer extends Player {	// 일부만 구현한 추상클래스
// abstract를 붙히지 않으면 에러 발생!(미완성)
	void play(int pos) { /* 내용생략 */ }	// 상속받은 메서드 중 1개만 구현(미완성 메서드)
//	abstract void stop(); 가 생략되어있음
}

 

  • 메서드 호출할 때는 선언부만 필요하기 때문에 추상 메서드도 호출이 가능하긴 함
  • 그러나 아무 기능이 없는 껍데기 이므로 보통은 상속을 통해 자손이 메서드를 완성시키고 객체생성 후 저장한 참조변수로 추상메서드 호출하여 정의한 기능을 수행하는 방식으로 사용함 -> 실무에서 자주 사용하는 인터페이스를 이런식으로 주로 사용하게됨
abstract class Player {	// 추상클래스
	boolean pause;
	int currentPos;

	Player() {	// 추상클래스의 생성자
		pause = flase;
		currentPos = 0;
    }

    // abstract를 써줌으로 인해 자손을 통해 메서드를 완성 시켜야 한다는 내용을 공유
    abstract void play(int pos);	// 추상메서드 -> 필수기능
    abstract void stop();	       // 추상메서드 -> 필수기능
    
    void play() {	// 추상클래스의 인스턴스 메서드
		play(currentPos);	// 추상메서드 호출
    }
}

 

(1) 예제

  • 일반적으로 추상메서드를  사용하는 예제
  • 추상 클래스인 Player를 AudioPlayer 클래스가 상속받아 추상메서드를 구현
  • 프로그램이 실행 될 때 객체를 생성하고 할당한 참조변수로 메서드를 호출하여 사용
abstract class Player{	// 추상클래스(미완성 설계도)
	abstract void play(int pos);	// 추상메서드(미완성메서드 - 선언부만있는 메서드)
	abstract void stop();	// 추상메서드
}

// 추상 클래스는 상속을 통해 완성해야 객체 생성 가능
class AudioPlayer extends Player {
    // 상속받은 메서드를 오버라이딩하여 재정의
	void play(int pos) {System.out.println(pos+"위치부터 재생합니다");}
	void stop() {System.out.println("재생을 멈춥니다");}
	
}

public class ex_7_PlayerTest {
	public static void main(String[] args) {
//		Player p = new Player();	        //추상 클래스의 객체를 그냥 생성 -> 에러!
		AudioPlayer ap = new AudioPlayer();	// 객체 생성
        
		// 자손객체에서 정의한 추상 메서드를 호출하여 사용
		ap.play(100);
		ap.stop();
        
		Player p = new AudioPlayer();	    // 다형성(조상의 객체로 변경 생성 - 가능)
		p.play(100);
		p.stop();
	}
}

2) 추상클래스의 작성

  • 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나, 기존클래스의 공통 부분을 뽑아서 추상클래스를 작성함
  • 비즈니스 로직상에서 Unit이라는 큰 카테고리의 추상 클래스를 만들어서 각 Unit의 하위분류인 Marine, Tank, Dropship의 클래스를 Unit을 구현하게 되면 공통 속성의 메서드를 중복해서 작성해야하는 번거로움을 줄일 수 있음

  • Unit에 있는 추상메서드 move덕분에 객체 배열에담겨있는 객체들마다 정의가 다르게 되어있는 move 메서드를 동시에 호출이 가능함 

(1) 예제

  • 이미지의 예제를 실제 구현한 예제
public class ex7_10 {
	public static void main(String[] args) {
    	// 객체배열(참조변수의 묶음)
		Unit[] group = { new Marine(), new Tank(), new Dropship() };

//		위의 코드를 풀어 쓰면?	
//		Unit[] group = new Unit[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);
		// group의 타입 = unit[]
		// group[0] ~ [2]의 타입 = unit

//		반목문을 풀어 쓰면?
//		group[0].move(100,200)	// Marine 객체의 move(100,200)을 호출
//		group[1].move(100,200)	// Tank 객체를 위와 같이 호출
//		group[2].move(100,200)	// Dropship 객체를 위와 같이 호출
		
		
//		만일 unit[]이 아닌 object[]로 바꾼다면? -> 에러발생
//		object클래스(최고조상 클래스)에는 move멤버가 없음 (unit클래스에 move가 있음)
//		object[] group = { new Marine(), new Tank(), new Dropship() };
	}
}

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

 

(2) 추상클래스의 장점

  • 코드 중복을 제거하고 작성을 쉽게 하고 관리(변경)가 용이해짐

  • 추상클래스를 의미있는 단계별로 생성해놓으면 중간단계의 클래스를 상속받아서 새로운 클래스를 구현이 가능
  • 추상화된 코드는 구체화된 코드보단 유연하여 변경에 유리(유지보수가 용이함)
  • 구체적 코드처럼 new로 객체를 생성해 버리면 메서드를 수정할 때마다 해당 메서드가 사용 된 모든 곳을 바꿔 줘야함
  • 추상적 코드처럼 상속하여 사용할 경우 createCalendar메서드만 수정하면 호출 된 모든 곳에 반영 -> 변경에 유리

 

 

** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의