
참고
자바의 정석
일전에 우리가 클래스를 배울 때 클래스를 일종의 설계도라고 하였다. 그렇다면 추상클래스는 미완성 설계도라고 할 수 있다.
클래스가 미완성이라는 것은 멤버의 개수에 관계된 것이 아니라 단지 미완성 메서드(추상 메서드)를 포함하고 있다는 의미이다. 또한 미완성 설계도로 제품을 만들지 못하듯이 추상 클래스로 인스턴스를 생성하지 못한다. 추상 클래스는 상속을 통해서 하위 클래스에 의해서 완성이 된다.
그러면 우리는 왜? 추상클래스를 만들어야 할까? 추상클래스 자체로는 클래스로서의 역할이 없지만 새로운 클래스를 작성하는데 바탕이 된다는 점이 엄청 중요하다. 실제 예를 들어보자. 스마트폰에는 여러 종류가 있지만 아마 스마트폰은 공통적인 기능들이 있을 것이다. 그 공통적인 기능들을 추상클래스에 설계하고 그것을 상속받는 갤럭시, 아이폰.. 등 각 하위 클래스의 독특한 특징을 만드는 것이 효율적일 것이다.
추상 클래스 선언은 class 앞에다가 abstract이라는 키워드만 붙여주면 된다. abstract키워드만 보고 추상 메서드가 있다는 걸 짐작하고 이 클래스를 상속에 잘 이용할 수 있을 것이다.
abstract class 클래스이름 {
}
추상클래스는 일반 클래스와 큰 차이는 단지 추상 메서드가 있다는 것 말고는 동일하다. 생성자도 있으며 멤버변수와 메서드도 존재한다.
💡 참고
추상메서드를 포함하고 있지 않는 클래스라도 키워드 abstract을 붙여서 추상클래스로 지정할 수도 있다. 추상메서드가 없는 완성된 클래스라도 추상클래스로 지정되면 인스턴스를 생성하지 못한다.
메서드는 일반적으로 선언부와 구현부가 존재한다. 그런데 추상 메서드는 선언부만 존재하고 구현부는 작성하지 않는 채로 남겨 둔 것을 의미한다. 즉, 미완성 메서드인것이다.
메서드를 미완성으로 두는 이유는 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문에 상위 클래스에서 선언만 해두고 실제 내용은 상속받는 클래스에 구현하도록 비워두는 것이다.
추상클래스로부터 상속받은 하위 클래스는 오버라이딩을 통해 추상메서드를 모두 구현해줘야 한다. 그렇지 않는다면 그 역시 추상 클래스로 지정해줘야 한다.
abstract class Player {
abstract void player(int pos);
abstract void stop();
}
class AudioPlayer extends Player {
void play(int pos) {/* 내용 생략 */}
void stop() {/* 내용 생략 */}
}
abstract class AbstractPlayer extends Player {
void play(int pos) {/* 내용 생략 */}
}
여러 클래스에 공통적인 부분의 클래스를 뽑아 추상클래스로 만들어 상속하도록 하는 경우가 있다.
상속이 하위 클래스를 만드는데 상위 클래스를 사용하는 것이라면, 이와 반대로 추상화는 기존의 클래스의 공통부분을 뽑아내서 상위 클래스를 만드는 것이라고 할 수 있다.
추상화: 클래스간의 공통점을 찾아내서 공통의 클래스를 만드는 것 (추상 클래스)
구체화: 상속을 통해 클래스를 구현, 확장하는 작업 (상속)
그러면 여기서 한 가지 생각해 볼 점이 있다. 추상메서드는 애초에 {}것을 한 메서드와 역할은 같아 보이는데 왜 {}를 사용안 하고 추상메서드로 정의하는 것일까? 정답은 하위 클래스에서 반드시 구현하도록 하는 강제성에 있다. 만일 추상메서드로 정의되어 있지 않고 몸통만 있는 메서드로 정의한다면 하위 클래스에서는 이 메서드를 굳이 구현안하고 넘어 갈수 있기 때문이다. 그래서 반드시 구현해야하는 것으로 추상메서드로 정의를 해야한다.