자바의 정석 ch7.6 추상 클래스(abstract class)

해로(김선호)·2022년 2월 1일
0

Java 문법

목록 보기
3/15

📔 본 포스팅은 자바의 정석(남궁성 저, 3판)을 읽고 정리한 내용입니다.

6.1 추상 클래스란?

  • 미완성 설계도. ‘추상 메서드(몸통이 없는 미완성 메서드)’를 포함한 클래스
    • 구현부가 있는 일반 메서드도 있을 수 있음
abstract class Player{ // 추상클래스 (미완성 클래스)
	abstract void play(int pos); // 추상 메서드 (몸통 {}이 없는 메서드)
	abstract void stop(); // 추상 메서드
	void move() { /* 생략 */ } 
}

추상 클래스의 인스턴스는 생성 불가능하다

  • 추상 클래스는 다른 클래스 작성에 도움을 주는 용도. 인스턴스 생성 불가능!
Player p = new Player(); // 에러. 추상 클래스의 인스턴스 생성 불가
  • 상속을 통해 ‘모든’ 추상 메서드를 완성(구현)해야 인스턴스 생성 가능
class AudioPlayer extends Player{ // 추상 클래스 Player의 추상 메서드 2개를 모두 구현
	void play(int pos) { // 추상 메서드를 구현(완성)
		/* 내용생략 */
	}
	void stop() { // 추상 메서드를 구현(완성)
		/* 내용 생략 */
	}
}

// 완성한 클래스는 인스턴스 생성 가능 
AudioPlayer ap1 = new AudioPlayer(); // OK
Player ap2 = new Player(); // OK. 다형성의 정의. 참조변수는 리모컨만 제공

6.2 추상 메서드(abstract method)

  • 미완성 메서드. 구현부(몸통, {})가 없는 메서드
    • 메서드는 선언부와 구현부로 구성. 추상 메서드는 선언부만 작성한 것
  • 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우 사용함

추상 메서드 작성방법

/* 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다. */
abstract 리턴타입 메서드이름();

추상 메서드를 일부만 구현한 경우에는 상속받은 자식 클래스에 접근 제어자 abstract를 붙인다

abstract class Player{ // 추상클래스 (미완성 클래ㅡㅅ)
	abstract void play(int pos); // 추상 메서드 (몸통 {}이 없는 메서드)
	abstract void stop(); // 추상 메서드
}

// 추상 클래스 Player의 추상 메서드 2개를 모두 구현
class AudioPlayer extends Player{ 
	void play(int pos) { // 추상 메서드를 구현(완성)
		/* 내용생략 */
	}
	void stop() { // 추상 메서드를 구현(완성)
		/* 내용 생략 */
	}
}

// 추상 클래스 Player의 추상 메서드 중 1개만 구현
abstract class AbstractPlayer extends Player {
	void play(int pos) { // 추상 메서드를 구현(완성)
		/* 내용생략 */
	}
	// 사실은 상속받은 추상메서드 stop()이 있음
}
  • 상속받은 클래스에서 상속받은 추상 메서드를 최소 1개 이상 구현하지 않은 경우에는 abstract를 붙여줘야한다.
    • abstract를 붙여 클래스 내부에 구현하지 않은 추상 메서드가 있음을 알린다
  • 추상 메서드 앞에 abstract를 붙임으로써, 자식 클래스는 이 추상 메서드를 반드시 구현해야하는 강제성을 갖게 된다.

추상 메서드의 호출은 가능하다(메서드 호출은 선언부만 알면 가능함)

  • 추상 클래스 내부에서, 추상 메서드가 호출되게끔 코드 작성이 가능하다. (메서드가 실제로 호출되는 시점에서는 이미 메서드가 완성되어 있다.)
    • 메서드를 호출한다는 것은 이미 인스턴스가 생성되었다는 뜻이고, 인스턴스가 생성되었다는 것은 기존의 추상 메서드가 구현 완료되었다는 뜻이므로, 구현된 메서드는 당연히 정상적으로 호출이 가능하다.
abstract class Player {
	boolean pause;
	int currentPos;

	Player() {
		pause = false;
		currentPos = 0;
	}
	/** 지정된 위치(pos)에서 재생을 시작하는 기능이 수행되도록 작성되어야 한다.*/
	abstract void play(int pos); // 추상 메서드
	/** 재생을 즉시 멈추는 기능이 수행되도록 작성되어야 함*/
	abstract void pause(); // 추상 메서드
	
	void play() {
		play(currentPos); // 추상메서드를 사용할 수 있다.
	}
}

6.3 추상 클래스의 작성

  • 여러 클래스에 공통적으로 사용될 수 있는 추상 클래스를 바로 작성하거나 기존 클래스의 공통 부분을 뽑아서 추상 클래스를 만든다 (나중에 리모컨의 버튼으로 활용)

리모컨의 버튼을 위한 추상 클래스

abstract class Unit {
    int x, y;
    abstract void move(int x, int y); // 리모컨의 버튼으로 사용
    void stop() {/* 현재 위치에 정지*/}
}

class Marine extends Unit {
    @Override
    void move(int x, int y) { /* 지정된 위치로 이동 */ }
    void stimPack() {}
}

class Tank extends Unit {
    @Override
    void move(int x, int y) {}
    void changeMode() {}
}

class Dropship extends Unit {
    @Override
    void move(int x, int y) {}
}
  • 위 클래스들의 사용을 위해 Unit[] 으로 배열을 선언하는 경우와 Object[] 배열을 선언할 때의 비교
		Unit[] group = new Unit[3]; // gruop의 타입은 Unit[], Unit[]은 객체배열.
        group[0] = new Marine();
        group[1] = new Tank();
        group[2] = new Dropship();

        for(int i = 0; i< group.length; i++){
						// group[i]는 Unit타입임
            group[i].move(100, 200); // Unit 리모컨의 move() 버튼 사용
        }

		// 위의 코드와 비교
		Object[] 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); // 에러. Object 리모컨에는 move()버튼이 없음
        }

(참고) 객체 배열 : 참조변수 묶은 것

객체지향개념에서의 추상화

  • 추상화(애매, 불명확한 것) ↔ 구체화 (명확, 구체적인 것)
  • 추상화된 코드는 구체화된 코드보다 유연함. 변경(말 바꾸기)에 유리
// 1. 내가 GregorianCalendar 객체를 사용할 것이 분명함 -> 구체적
GregorianCalendar cal = new GregorianCalendar(); // 구체적

// 2. getInstance메서드가 정확하게 '무슨' 객체를 반환할지 모름 -> 불명확. 추상적 
Calendar cal = Calendar.getInstance(); // Calendar의 자손 객체를 반환. 추상적  

6.4 추상 클래스의 활용(일부 다형성 보장)

추상 클래스는 기능이 비슷한 클래스들을 한 데 묶는 조상 역할을 한다

다형성의 보장이란?

  • 부모가 하위 클래스에 동작 명령을 내렸을 때 하위 클래스가 반드시 동작을 하게 하는 것
    • abstract 로 선언된 추상 메서드를 하위 클래스에서 반드시 오버라이딩함

추상 클래스의 활용

  • 서로 기능이 비슷한 클래스들을 묶을 필요가 있을 때 사용함(서로 기능이 다른 클래스들을 묶을 때는 인터페이스를 사용)
public abstract class Animal {
	public abstract void eat();

	public void move() {
		System.out.println("무리를 지어서 이동한다.");
	}
}

public class Dog extends Animal {
	public void eat() {
		System.out.println("개처럼 먹다");
	}
}

public class Cat extends Animal {
	public void eat() {
		System.out.println("고양이처럼 먹다")
	}
}
  • Dog클래스와 Cat클래스는 기능이 비슷함
    • 같은 기능인 move()는 따로 오버라이딩 하지 않고 조상 멤버를 그대로 사용.
    • 다르게 동작해야할 필요가 있는 eat() 추상메서드만 오버라이딩하여 사용하였음(= 일부 다형성 보장)
profile
Every Run, Learn Counts.

0개의 댓글