19. 추상화(abstract)
추상화(abstract)?
지난 글 중에서 제어자(modifier) 글에서 간략하게 살펴봤던 추상화(abstract)는 공통된 특징들을 묶어서 하나의 클래스로 정의하는 것을
의미합니다. 하지만 추상화를 처음 접하는 입장에서 이 간단한 정의로는 이해가 잘 안 되기 때문에 천천히 살펴나가도록 하겠습니다.
먼저 추상 클래스에 대해 알아보겠습니다. 추상 클래스는 크게 보면 미완성 설계도라고 할 수 있고, 정확하게는 미완성 메서드인 추상
메서드를 포함하는 클래스를 의미합니다. 이 미완성의 추상 클래스는 다른 클래스를 정의하는 것에 도움을 주기 위한 용도이기 때문에
해당 클래스를 직접 인스턴스화 하는 것은 불가능합니다. 따라서 상속을 통해 포함하고 있는 추상 메서드(들)를 완성해야 인스턴스
생성이 가능해집니다. 또 추상 클래스는 변수, 생성자, 메서드들을 포함할 수 있습니다.(이후 다룰 인터페이스와 차이가 나는 부분중 하나)
다음으로 추상 메서드에 대해 알아보겠습니다. 추상 메서드는 마찬가지로 미완성 된 메서드라고 할 수 있고, 정확하게는 구현부 {} 가
존재하지 않는 메서드를 의미합니다. 상속받는 자식 클래스들 마다 이 메서드가 다르게 구현될 것으로 예상될 때에 필요합니다.
(따라서 추상 메서드에 대해 어떤 기능을 수행할 것인지에 대한 주석 작성을 해주게 되면 좀 더 올바르게 정의하여 사용할 수 있습니다!)
추상 메서드의 경우에는 추상 클래스와 달리 구현부가 없이 선언부만 있어도 호출이 가능합니다. 그 이유는 추상 클래스는 직접 인스턴스화
해 사용하는 것이 불가능하고 자식 클래스가 상속받아 사용되는데 이 과정에서 결국 해당 추상 메서드들이 완성될 것이기 때문입니다!
다음으로 이에 대한 예제를 살펴보겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
|
package com.vanilla.javaStudy;
abstract class Animal { // 추상클래스는 추상 메서드를 포함하고 있는 클래스
String name;
Animal(String name) {
this.name = name;
}
// 동물마다의 울음소리에 대한 메서드
abstract void sound(); // 추상 메서드 구현부 미존재
// 동물마다의 소개에 대한 메서드
void intro() {
System.out.println("저는 " + name + "입니다!");
}
}
class Dog extends Animal {
Dog(String name) {
super(name);
}
void sound() {
System.out.println("멍멍!");
}
}
class Cat extends Animal {
Cat(String name) {
super(name);
}
void sound() {
System.out.println("야옹!");
}
}
public class abstractExam {
public static void main(String[] args) {
Dog D = new Dog("강아지");
D.sound();
D.intro();
Cat C = new Cat("고양이");
C.sound();
C.intro();
Animal A = new Dog("강아지");
A.sound();
A.intro();
}
}
|
cs |
3행을 보시면 클래스 Animal은 앞에 abstract 키워드가 붙었고, 실제로 내부에 추상 메서드 sound()가 존재하므로 추상 클래스임을 알 수
있습니다.
12행에서 추상 메서드가 존재하고, 이 추상 메서드는 자식 클래스에 상속되어 정의되어야 합니다. (따라서 결국은 정의되어
사용될 메서드이므로 해당 클래스에 호출 또한 가능합니다.)
Dog 클래스와 Cat 클래스에서 각각 해당 메서드들이 정의되었음을 알 수 있고, 각 동물마다의 울음소리는 다르기 때문에
추상 메서드들을 사용했음을 알 수 있습니다.
main 메서드에서 각 클래스들에 대한 인스턴스를 생성하여 사용했음을 볼 수 있고, 이때 58행과 같이 부모타입 Animal로 참조변수 A를
통해 Dog 인스턴스를 가리키는 것 또한 물론 가능합니다.
(헷갈리신다면 리모컨을 기억! Dog 클래스는 Animal 클래스를 상속받았으므로 Animal 클래스보다 기능은 같거나 많습니다.
따라서 Animal 인스턴스를 가리키는 리모컨이라고 하더라도 Dog 인스턴스에는 이 기능들이 모두 있으므로 문제없이 사용 가능합니다.)
추상화의 장점
위에서 추상화는 공통된 특징들을 묶어서 하나의 클래스로 정의하는 것이라고 하였습니다. 그런데 위 예제에서는 이해를 위해 대략적인
코드들로 구성돼 있어 아직 그 장점에 대한 감이 안 오실 것이라 생각합니다. 따라서 아래 코드와 비교하여 살펴보도록 하겠습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
|
package com.vanilla.javaStudy;
class Dog2 {
String name;
Dog2(String name) {
this.name = name;
}
void sound() {
System.out.println("멍멍!");
}
void intro() {
System.out.println("저는 " + this.name + "입니다!");
}
}
class Cat2 {
String name;
Cat2(String name) {
this.name = name;
}
void sound() {
System.out.println("야옹!");
}
void intro() {
System.out.println("저는 " + this.name + "입니다!");
}
}
public class abstractExam2 {
public static void main(String[] args) {
Dog2 D = new Dog2("강아지");
D.sound();
D.intro();
Cat2 C = new Cat2("고양이");
C.sound();
C.intro();
}
}
|
cs |
(( 우선 바로 위 예제와 우리가 먼저 다루었던 예제 코드를 비교해가며 어떠한 차이가 있는지 살펴보시기를 권장드립니다! ))
위 예제를 살펴보시면 우선 String name; 을 통해 변수 선언에 대한 행이 중복되고 있습니다. 또한 super() 생성자를 통해 부모 클래스에서
초기화시켜 사용하는 intro() 메서드 또한 중복되고 있음을 볼 수 있습니다. 만약 이러한 변수 선언과 메서드 선언이 지금처럼 한 개가 아닌
수없이 많이 존재한다면 어떨까요? 일괄 수정해야할 때 우리는 클래스 마다 해당 변수들과 메서드들을 모두 수정해주어야 할 것입니다.
하지만 공통된 특징들끼리 묶어 추상 클래스를 선언하였다면, 추상 클래스 내 선언된 부분들만 수정해준다면 상속받은 자식 클래스들은
모두 일괄 수정된다는 첫 번째 장점이 있습니다. 앞서 다룬 예제의 코드처럼 Animal 클래스 내 intro() 메서드만 수정해준다면 단순 호출
해서 사용하고 있기 때문에 모든 자식 클래스들은 문제없이 수정되어도 사용할 수 있다는 것을 보실 수 있습니다!
즉 정리해보면 코드의 중복 제거, 가독성 향상, 유지보수 용이 등의 장점들을 가질 수 있게 됩니다!
두 번째 장점은 해당 추상 메서드들은 상속받아 사용할 때 정의해주지 않으면 에러를 발생시키기 때문에 이에 대한 정의를 강제시킬 수
있다는 것입니다. 우리가 부모 클래스에서 위의 sound() 메서드를 단순히 sound() {} 처럼 구현부를 비어두고 정의하여 이를 상속받을 때
일일이 오버라이딩 하는 방식으로 사용하기를 기대하고 작성했다고 해보겠습니다. 그런데 사용자가 이러한 흐름을 이해하지 못하고
있다면 sound() 메서드를 미처 오버라이딩 하지 못하는 경우가 생길 수 있고, 이는 단순 에러를 발생시킬 수도 있고
심한 경우 문제없이 사용하다가 어느 날 누적되온 이런 거대한 문제가 터질 수 있다는 위험이 발생할 수도 있습니다. 따라서 정의를
강제한다는 것은 이러한 장점을 가질 수도 있겠습니다!