추상클래스란? 

  • 클래스설계도에 비유한다면, 추상클래스미완성 설계도에 비유할 수 있습니다. 
  • 추상클래스미완성이라는 것은 맴버의 개수에 관계된 것이 아닌, 단지 미완성 메서드( 추상메서드 )를 포함하고 있다는 의미입니다. ( 추상메서드가 없더라도 인스터스 생성을 제한하기 위해 추상클래스로 만들 수 있습니다. )
  • 추상클래스미완성 클래스이므로 인스턴스생성할 수 없으며, 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 갖습니다. 
  • 추상클래스클래스 선언부에 키워드 'abstract'를 붙여서 선언할 수 있습니다.
abstract class 클래스이름{
	... // 추상메서드, 생성자, 멤버변수, 메서드 등 
    	// 추상메서드의 유무를 제외하면 일반 클래스와 동일한 맴버를 가질 수 있음.
}

추상메서드 ( abstract method ) 

  • 메서드선언부구현부구성되어 있습니다. 메서드선언부만 작성하고 구현부는 작성하지 않는 채로 남겨 둔 것이 바로 추상메서드입니다.
  • 추상메서드의 실제 구현상속받는 클래스에 따라 달라질 수 있으며, 조상클래스메서드 선언부만을 작성하고 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려 주어 자손클래스가 상황에 맞게 구현하도록 해주어야 합니다. 
// 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명합니다.
abstract 리턴타입 메서드이름();

 

  • 추상클래스상속받는 자손클래스오버라이딩을 통해 추상메서드모두 구현해주어야 합니다.
  • 모든 추상메서드구현하지 않으면 자손클래스 역시 추상클래스로 지정해 주어야 합니다.

추상클래스 작성 - 추상화와 구체화

  • 상속자손클래스를 만드는데 조상클래스를 사용하는 것이라면, 이와 반대로 추상화는 기존의 클래스공통부분을 뽑아내서 조상클래스로 만드는 것이라고 할 수 있습니다.
  • 추상화구체화와 반대되는 의미로 이해할 수 있으며, 상속계층을 따라 내려갈수록 클래스는 점점 기능이 추가되어 구체화의 정도가 심해지며, 상속계층도를 따라 올라갈수록 추상화의 정도가 심해진다고 할 수 있습니다.
추상화 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
구체화 상속을 통해 클래스를 구현, 확장하는 작업

추상클래스 작성하기 ( 1 )

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 = false;      // pause를 false로 바꾸고,
            play(currentPos);   // 현재의 위치에서 play
        }
        else {
            pause = true;       // puase 상태를 true로 바꾸고,
            stop();             // play를 멈춥니다.
        }
    }
}

class CDPlayer extends Player {

    @Override
    void play(int currentPos) {
        /* 조상의 추상메서드 구현 */
    }

    @Override
    void stop() {
        /* 조상의 추상메서드 구현 */
    }
    
    // CDPlayer 클래스에 새로 추가된 멤버 
    int currentTrack;   // 현재 트렉
    void nextTrack() {  
        currentTrack++;
    }
}
  • 조상클래스추상메서드CDPlayer 클래스의 기능에 맞게 완성해주고, CDPlayer만의 새로운 기능들을 추가한 예시입니다. 
  • 사실 다음과 같이 추상메서드를 아무 내용도 일반메서드로 작성하고, 자손클래스에서 오버라이딩하여 자신의 클래스에 맞게 구현할 수도 있습니다.
class Player{
	...
    void play(int pos){}	// 아무런 내용도 없는 빈 일반메서드	
    void stop(){}			// 아무런 내용도 없는 빈 일반메서드
    ...
}

 

  • 굳이 abstract를 붙여서 추상메서드로 선언하는 이유는 자손클래스에서 추상메서드반드시 구현하도록 강요하기 위해서입니다.
  • 위와 같이 몸통만 가지도록 정의되어 있는 경우, 자손클래스에서 이 메서드들이 온전히 구현된 것으로 인식하고 오버라이딩을 통해 자신의 클래스에 맞게 구현하지 않을 수도 있기 때문에 이를 방지하고자 추상메서드를 사용하는 것입니다.

추상클래스 작성하기 ( 2 )

  • 기존의 클래스로부터 공통된 부분을 뽑아서 추상클래스를 만들어 보겠습니다. 
 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 클래스는 새로운 유닛을 위한 클래스를 작성하는 데 재활용될 수 있을 것입니다.
  • 또한, 각 자손클래스들의 공통 조상인 Unit 클래스 타입의 참조변수 배열을 통해서 다음과 같이 서로 다른 종류의 인스턴스를 하나의 묶음으로 다룰 수 있을 것입니다.
Unit[] group = new Unit[4];
group[0] = new Marine();
group[1] = new Tank();
group[2] = new Marine();
group[3] = new DropShip();

for(Unit u : group){
	u.move(100, 200);		// Unit 배열의 모든 유닛을 좌표 ( 100, 200 )의 위치로 이동한다.
}

 

  • Unit 클래스move 메서드추상 메서드로 정의되어 있더라도, 메서드참조변수의 타입에 관계없이 실제 인스턴스에 구현된 것이 호출되기 때문에 Unit 클래스 타입의 참조변수로 move 메서드를 호출하는 것이 가능합니다. ( 각 인스턴스마다 Marine, Tank, DropShip에서 구체화 move 메서드가 실행됩니다. )  

+ Recent posts