티스토리 뷰

728x90
반응형

추상화

 

추상화는 앞서 알아본 상속, 캡슐화, 다형성과 함께 객체지향 프로그래밍의 네 가지 기둥 중 하나이다.

 

자바에서의 추상화는 객체의 공통적인 속성과 기능을 추출하여 정의하는 것을 말한다.

 

이전에 살펴본 상속이 상위 클래스를 이용해 하위 클래스를 정의하는 것이라고 한다면,

 

추상화는 반대로 기존 클래스들의 공통점을 뽑아서 상위 클래스를 만들어내는 것이라고 할 수 있다.

 

위 그림은 추상화에 대해 잘 나타내고 있다.

 

자동차와 오토바이의 공통된 기능을 추출해 Vehicle이라는 클래스에 담았다고 생각하면 된다.

 

이런 식으로 공통적인 속성과 기능을 모아주면 코드의 중복을 줄일 수 있고,

 

클래스 간의 관계설정도 쉬워지며 유지/보수가 용이해진다.

 

자바에서의 추상화는 두 가지 문법요소를 이용해 구현되는데,

 

추상 클래스 인터페이스가 그것이다.

 


abstract 제어자

 

앞선 <접근 제어자(Access Modifier)> 글에서 제어자는 크게 두 종류로 나뉨을 알아봤었다.

 

2022.07.15 - [Development/Java] - [Java]접근 제어자(Access Modifier)

 

[Java]접근 제어자(Access Modifier)

자바 프로그래밍에서 제어자(Modifier)는 클래스, 필드, 메서드, 생성자 등에 부가적인 의미를 부여하는 키워드를 말한다. '파란 하늘', '붉은 노을'에서 '파란'과 '붉은' 처럼 명사를 꾸며주는 

gnidinger.tistory.com

접근 제어자와 기타 제어자가 그것이었는데,

 

이 글에선 기타 제어자 중 abstract 제어자에 대해 알아본다.

 

abstract는 '추상적인'이라는 뜻을 지닌 형용사인데,

 

자바의 맥락에서 abstract는 '미완성의'라는 뜻이라고 봐도 무방하다.

 

abstract는 주로 메서드와 클래스를 정의할 때 사용되는 제어자로 사용되는데

 

메서드 앞에 붙은 경우를 '추상 메서드(abstract method)', 클래스 앞에 붙은 경우를 '추상 클래스(abstract class)'라고 한다.

 

또한 어떤 클래스에 추상 메서드가 하나라도 포함되어 있는 경우 해당 클래스는 자동으로 추상 클래스가 된다.

 

추상 메서드와 추상 클래스에 대해 예를 들어보자.

abstract class AbstractExample { // 추상 메서드가 최소 하나 이상 포함돼있는 추상 클래스
	abstract void start(); // 메서드 바디가 없는 추상메서드
}

AbstractExample이라는 추상 클래스 안에 start라는 추상 메서드가 정의되어 있음을 볼 수 있다.

 

앞서 abstract의 맥락이 '미완성'이라고 말한 대로 추상 메서드엔 시그니처만 있고 바디가 없다.

 

그 앞에 abstract 키워드가 붙어 해당 메서드가 추상 메서드임을 표시한다.

 

즉, 추상 메서드 '미완성 메서드'라고 불려도 틀리지 않으며,

 

추상 메서드를 포함하는 클래스는 '미완성 클래스'를 의미하는 추상 클래스가 된다고 할 수 있다.

 

마지막으로 추상 클래스는 미완성 클래스, 즉 미완성 설계도이기 때문에

 

이를 기반으로 객체 생성이 불가능하다는 특징이 있다.

AbstractExample abstractExample = new AbstractExample(); // 에러발생

추상 클래스

 

그렇다면 객체도 생성하지 못하는 미완성 설계도를 왜 만드는 것일까?

 

여기엔 크게 두 가지 이유가 있다.

 

  1. 추상 클래스는 상속 관계에 있어 새로운 클래스를 작성하는데 매우 유용하다.
  2. 설계하는 상황이 변하더라도 보다 유연하게 대응할 수 있다.

2번에 대해 조금 더 설명을 하자면, 선언부를 상위 클래스에 만들고 구체적인 구현을 상속받는 하위 클래스에서

 

하도록 한다면, 상황에 맞는 메서드 구현이 가능하게 된다는 뜻이다.

 

이때 자주 사용하게 되는 개념이 상속 파트에서 알아봤던 오버라이딩인데,

 

이 오버라이딩을 통해 상속받은 추상 메서드를 구현해 완성시킬 수 있고

 

이렇게 완성된 클래스를 기반으로 객체를 생성할 수 있기 때문이다.

 

예를 들면 다음과 같다.

abstract class Animal {
	public String kind;
	public abstract void sound();
}

class Dog extends Animal { // Animal 클래스로부터 상속
	public Dog() {
		this.kind = "포유류";
	}

	public void sound() { // 메서드 오버라이딩 -> 구현부 완성
		System.out.println("멍멍");
	}
}

class Cat extends Animal { // Animal 클래스로부터 상속
	public Cat() {
		this.kind = "포유류";
	}

	public void sound() { // 메서드 오버라이딩 -> 구현부 완성
		System.out.println("야옹");
	}
}

class DogExample {       
    public static void main(String[] args) throws Exception {
       Animal dog = new Dog();
       dog.sound();

       Cat cat = new Cat();
       cat.sound();
    }
 }

// 출력 결과
멍멍
야옹

 먼저 추상 클래스 Animal 안에 추상 메서드 sound()가 정의되어 있다.

 

이어서 Animal 클래스를 상속받은 Dog 클래스와 Cat 클래스는 각각 필요에 따라

 

sound() 메서드를 오버라이딩으로 완성시켰고 이 클래스들을 기반으로 메인 메서드에서 dog와 cat 객체를 생성했다.

 

생성된 객체 안에서 sound() 메서드를 호출한 결과 "멍멍", "야옹"과 같이 의도된 결과가 나온 것을 알 수 있다.

 

이렇게 상황에 맞는 메서드를 구현함으로써 추상 클래스는 추상화를 구현하는데 핵심적인 역할을 수행한다.

 

또한 여러 사람이 함께 개발하는 환경에서 공통된 속성과 기능을 다른 변수와 메서드로 정의하는 오류를 미연에 방지할 수 있는데

 

이는 공통적인 특성들을 모아 먼저 추상 클래스로 선언해주면 되기 때문이다.

 

따라서 구체화에 반대되는 개념으로 추상화를 이해하면,

 

상속 계층도의 상층부에 위치할수록 추상화의 정도가 높고 아래로 내려올수록 구체화된다고 쓸 수도 있다.

 

또는, 상층부에 가까울수록 더 공통적인 속성과 기능들이 정의되어 있다고 생각할 수도 있다.

 

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/09   »
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
글 보관함