추상 클래스의 문법적인 특징이나, 객체 생성이 되고 안되고 이런 특징들이 중요한 게 아니다.
추상 클래스가 무엇이고 왜 사용하는지, 본질적인 개념부터 알아야 연쇄적으로 자연스럽게 추상 클래스를 이해할 수 있다
그 이유는, 추상 클래스의 본질을 이해하는 순간 문법적인 부분은 외우지 않아도 추상 클래스 철학에 맞게 녹아져 있다
1. 추상 클래스 개념
추상 클래스는 도대체 뭘까?
결론부터 말하면 A클래스, B클래스, C클래스가 있다고 가정을 해보자
여기서 각 클래스 안에는 각자의 필드와 메서드가 있을 것이다. 추상클래스는 A클래스, B클래스, C클래스들 간에 비슷한 필드와 메서드를 추출해 만들어진 클래스이다
예를 들어 '키보드'라는 클래스가 있다.
'키보드'를 만드는 제조사는 여러 개다. A 제조사, B제조사, C제조사는 각 제조사만의 스타일대로 키보드를 제작하고 소비자들에게 제품을 출시한다.
A 제조사는 키보드를 누를 때마다 빛이 들어온다.
B제조사는 누를 때마다 딸깍거리는 소리가 난다.
C제조사는 누를 때 살짝만 눌러도 잘 눌린다.
여기서 이 키보드들 간에 공통점이 뭐가 있을까? 바로 키보드를 누른다라는 액션 즉, 메서드가 공통적이다
그럼 이 메서드를 추출해서 추상 클래스 안에 두면 된다
(키보드를 상속받아 탄생한 A키보드, B키보드, C키보드)
다른 예를 들어 보겠다. 나의 얼굴과 너의 얼굴이 있다. 우리는 일반적으로 귀 2개, 눈 2개, 입 1개, 코 1개를 지니고 있다
여기서 왼쪽 눈, 오른쪽 눈, 왼쪽 귀, 오른쪽 귀, 입, 코 이 변수들은 공통적인 변수이다
이 변수들을 추상 클래스에 넣는다
다음으로 너가 웃고 내가 웃는다 우리는 공통적으로 웃고 있지만, 나는 웃을 때 덧니가 보이고, 너는 웃을 때 입을 가리고 웃는다. 각자 자기만에 스타일대로 웃고 있다. 하지만 우리는 공통적으로 웃는다 라는 액션 즉, 메서드를 가지고 있다.
이 웃는다 라는 메서드를 추상 클래스에 넣는다
(인간을 상속받아 탄생한 '나'라는 클래스, '너'라는 클래스)
실체 클래스는 실체가 드러나는 클래스
추상 클래스는 실체 클래스의 공통적인 부분을 추출해 어느 정도 규격을 잡아놓은 추상적인 클래스이다.
그래서 실체 클래스 실제 객체를 생성할 정도의 구체성을 가지는 반면, 추상 클래스는 아직 메서드와 내용이 추상적이기 때문에 객체를 생성할 수 없다
객체를 직접 생성할 수 있는 클래스를 실체 클래스라고 하며, 실체 클래스들의 공통적인 특성을 추출해서 선언한 클래스를 추상 클래스라고 한다
여기서 추상 클래스와 실체 클래스는 상속적인 관계를 가지고 있다
1. 추상 클래스는 실체 클래스의 공통적인 부분을 추출해서 선언한 클래스
2. 추상 클래스는 실체성이 없고 구체적이지 않기 때문에 객체를 생성할 수 없다
3. 추상 클래스와 실체 클래스는 상속관계이다
2. 추상 클래스 용도 (중요)
그렇다면 추상 클래스를 사용하는 이유는 뭘까?
크게 세 가지가 있다
● 공통된 필드와 메서드를 통일한 목적
10명 개발자들에게 자동차를 상속받아 각자만의 실체 클래스를 구현하라고 주문해보자
10명의 개발자가 생각한 변수명과 메서드명은 제각기 다른 이름을 가지고 구현될 것이다
이렇게 구현이 되면 문제가 있다. 만약, 수만 줄에 이르는 코드에 A라는 자동차 실체 클래스 객체를 선언하고 해당 객체의 필드와 메서드를 떡칠했다고 치자. 헌데 A자동차가 계약 만료가 되고, B자동차를 새로 교체해야 한다고 하자
느낌이 오는가? 만약 B자동차의 변수와 메서드명이 A자동차와 동일하면 객체 인스턴스만 변경하면 된다
만약에 동일하지 않다면 필드와 메서드를 전부다 일일이 찾아서 변경해줘야 한다
유지보수는 개뿔, 이건 새로 개발하는 느낌일 것이다. 따라서 추상 클래스를 만든다
추상 클래스에서 미리 정의한 필드와 메서드가 있다면, 실체 클래스는 추상 클래스의 필드와 메서드명을 변경할 수 없고 무조건 해당 명명으로 구현해야 한다. 따라서 필드와 메서드 이름을 통일하여 유지보수성을 높이고 통일성을 유지할 수 있다
● 실체 클래스 구현 시, 시간 절약
실무적으로 생각해보자! 갑자기 나보고 자동차라는 어마 무시한 클래스를 일주일 안에 구현하라고 한다
턱없이 시간이 부족할 것이다. 설계부터 생각한다. 자동차는 바퀴가 있어야 하고, 굴러가야 하고, 백미러도 있어야 한다
그리고 트렁크도 있어야 하고.....
느낌이 오는가? 여기서 추상 클래스는 효과를 발휘한다. 내가 자동차를 구현해야 하는데, 자동차 추상 클래스를 상속받으면, 자연스럽게 자동차에 공통적으로 들어가야 하는 필드와 메서드가 녹여져 있는 필드와 메서드가 딱 하고 오버라이딩 된다. 즉, 강제로 주어지는 필드와 메서드를 가지고 나만의 스타일대로 구현만 하면 된다. 설계 시간이 절약된다
그럼 여기서 잠깐! 그런 추상 클래스는 결국 누군가 설계해야 하는데 누가 하나요?
보통 당신이 취업하면 바로 그런 일을 안 한다. 그럼 누가 할까?
그런 설계적인 일만 하라고 있는 고급 개발자가 있다. 바로 AA!! 애플리케이션 아키텍처(application architecture)가 멋지게 설계해줄 거다. 그러니 우리는 추상 클래스가 어떤 건지 알고, 이를 상속받아서 멋지게 구현하면 되다는 사실을 기억하자!
● 규격에 맞는 실체 클래스 구현
위에 실체 클래스 구현 시, 시간 절약과 비슷할 수도 있는 내용이다. 하지만 따로 분류해서 설명해야 할 것 같아서 굳이 규격에 맞는 실체 클래스 구현이라고 했다
이미 설명했지만, 아무리 자기 스타일대로 클래스를 구현한다고 해도 그것도 결국엔 규격 안에서 구현하는 것을 허락한다는 것이지, 규격도 없이 아무렇게나 구현을 해서는 안된다. 왜냐? 혼자서 개발하는 일이 아니다
모두가 약속한 필드와 메서드 그리고 설계 규칙에 녹아져 있는 규격에 맞는 클래스를 구현해야 한다
그래야 코드 수정 시, 영향도를 적게 가져가면서 유지보수성을 높일 수 있다
따라서 선임 설계 개발자가 틀을 잡아주고, 해당 규격에 맞게 클래스를 구현하면 된다
여기서 추상 클래스의 강력한 기능이 나온다. 추상 클래스를 상속받은 실체 클래스들은 반드시!! 추상 메서드를 오버라이딩해서 실행 내용을 작성해야 한다
만약, 그렇지 않으면 컴파일 에러가를 발생시켜 실행조차 못하게 막는다. 따라서, 코더들은 강제적으로 추상 메서드를 구현해야 한다. 여기서 추상 메서드라는 것이 갑자기 나왔는데, 추상 클래스 안에 abstract키워드를 가지고 있는 메서드는 추상 메서드라고 하며, 상속 시 반드시 재정의해야 하는 메서드라는 뜻이다
3. 추상 클래스 문법
다음은 문법을 알아보자. 간단하다
클래스 앞에 abstract 키워드를 붙이면 추상 클래스이다
public abstract class 클래스명{
//필드
//생성자
//메서드
//추상메서드
}
추상 메서드도 리턴 타입 앞에 abstract키워드를 붙이면 된다
package ABSTRACTCLASS;
public abstract class Animal {
public String kind;
public void breath(){
System.out.println("숨 쉰다.");
}
//추상메서드
public abstract void sound();//구체적인 구현부는 없음!
}
Animal이라는 추상 클래스를 구현했다. Animal클래스 앞에 abstract 키워드가 있기 때문에 해당 클래스는 추상 클래스임을 알 수 있다. 내부를 보면 kind필드와 breath()라는 일반 메서드, 그리고 abstract가 붙은 sound() 추상 메서드가 있다
여기서 해당 추상 클래스를 상속받는 실체 클래스들은 반드시 sound()라는 추상 메서드를 상속받아 오버라이딩해야한다
package ABSTRACTCLASS;
public class Dog extends Animal{
public Dog(){
this.kind = "포유류";
}
@Override
public void sound() {
// TODO Auto-generated method stub
System.out.println("왈왈!");
}
}
extends 키워드를 통해, Animal 추상 클래스를 상속받은 Dog 실체 클래스이다
필드는 추상 클래스 필드를 그대로 사용했고, sound() 추상 메서드를 오버라이딩해서 구현했음을 알 수 있다
명확하게 @Override 어노테이션도 해당 메서드가 재정의 됐음을 알 수 있다
package ABSTRACTCLASS;
public class Cat extends Animal{
public Cat(){
this.kind = "포유류";
}
@Override
public void sound() {
// TODO Auto-generated method stub
System.out.println("야~옹!");
}
}
다음은 Cat 클래스이다. Dog 클래스와 똑같다. 다만, sound() 추상 메서드는 Cat실체클래스에 맞게 자기 스타일대로 구현되어 있음을 확인할 수 있다. 여기서 오버라이딩을 하면, 다형성이 발생된다는 사실을 알 수 있다.
또한, 규격에 맞게끔 필드명과 메서드명이 통일되어 있음을 알 수 있고, 실체 클래스들의 코드를 보면 모양은 비슷비슷하다. 즉, 규격이 맞춰져 있다는 뜻이다
※ 다형성: 같은 기능인데, 다른 결과를 도출할 수 있음
package ABSTRACTCLASS;
public class AnimalExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog dog = new Dog();
Cat cat = new Cat();
dog.sound();
cat.sound();
Animal animal = null;
animal = new Dog(); //자동 타입변환
animal.sound(); //Dog에 구현된 Sound()메서드 실행
animal = new Cat(); //자동 타입변환
animal.sound(); //Cat에 구현된 Sound()메서드 실행
animalSound(new Dog()); //자동 타입변환 (매개변수도 가능)
animalSound(new Cat()); //자동 타입변환 (매개변수도 가능)
}
//자동 타입변환 : 추상클래스 타입 변수는 추상클래스를 상속받은 실체클래스의 타입으로 자동 타입변환이 된다.
private static void animalSound(Animal animal) {
animal.sound();
}
}
//출력결과
//-------------
//왈왈!
//야~옹!
//왈왈!
//야~옹!
//왈왈!
//야~옹!
추상 클래스와 추상 클래스를 상속받아 구현한 실체 클래스를 어떻게 객체 생성을 하고 사용하는지 예제를 보자
Dog, Cat객체는 각 실체 클래스가 구현한 sound() 메서드가 실행된다. 따라서 왈왈! 야~옹!이라는 다른 결과를 도출한다
또한, 추상 클래스 변수에, 추상 클래스를 상속받아 구현한 실체 클래스 인스턴스를 주입하면 해당 추상 클래스 변수는 자동 타입 변환을 발생시켜 실체 클래스 인스턴스처럼 사용할 수 있다. 이를 타입의 다형성이라고 한다. 타입의 다형성은 animalSound()라는 메서드를 통해 매개변수도 타입의 다형성(자동 타입 변환)을 보여줄 수 있음을 확인할 수 있다
추상 클래스에 대해서 알아봤다
추상 클래스 개념, 용도, 문법 이렇게 세 가지를 설명했는데, 이 중에서 나는 용도가 가장 중요하다 생각한다
왜 사용하는지 생각하면 추상 클래스를 좀 더 이해하기 쉬울 거 같다
'Java' 카테고리의 다른 글
Java 자바 인터페이스란, 추상클래스 VS 인터페이스 (0) | 2020.08.27 |
---|