자바 추상화란? 추상 클래스와 인터페이스 차이까지 예제로 이해하기
지난 글에서는 객체지향 프로그래밍(OOP)의 핵심 개념 중 하나인 자바 캡슐화에 대해 알아보았다. 이번 글에서는 객체지향 프로그래밍의 또 다른 중요한 개념인 자바 추상화(Abstraction)에 대해 살펴보겠다.
추상화라는 단어는 처음 접하면 어렵게 느껴질 수 있지만, 개념 자체는 생각보다 단순하다. 추상화란 여러 대상의 공통적인 특징을 묶어 하나의 개념으로 정의하는 것이라고 이해하면 된다.

자바 추상화 개념 쉽게 이해하기
예를 들어 다음과 같은 동물이 있다고 가정해 보자.
- 강아지
- 고양이
- 양
- 소
이 동물들을 하나의 카테고리로 묶는다면 무엇으로 표현할 수 있을까? 가장 자연스러운 답은 “동물”이라는 개념이다. 객체지향 관점에서 보면 동물은 추상 클래스(abstract class)가 되고, 강아지·고양이·양·소는 동물 클래스를 상속받는 구체 클래스가 된다.
곧 동물이라는 추상적인 개념을 먼저 정의하고, 각 동물 클래스는 이를 상속(extends)받아 구현하는 것이다.
추상 클래스의 역할
조금 더 나아가 동물이라는 추상 클래스에는 동물이 공통으로 가지는 행동을 정의할 수 있다. 예를 들면 숨을 쉰다, 먹이를 먹는다, 소리를 낸다 같은 것들이다.
이 가운데 숨 쉬기와 먹기는 모든 동물이 비슷하니 추상 클래스에서 바로 구현해 두고, 동물마다 다른 울음소리는 추상 메서드로만 선언해 각 클래스에서 재정의(Override)하게 만들 수 있다. 실제로 동작하는 코드로 보면 다음과 같다.
abstract class Animal {
void breathe() {
System.out.println("숨을 쉰다");
}
void eat() {
System.out.println("먹이를 먹는다");
}
abstract void sound(); // 동물마다 다르므로 구현은 자식 클래스에 맡긴다
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("멍멍");
}
}
class Cat extends Animal {
@Override
void sound() {
System.out.println("야옹");
}
}
추상 클래스는 new Animal()처럼 직접 객체를 만들 수 없고, 반드시 상속받은 클래스로 만들어야 한다. 그리고 자식 클래스는 추상 메서드인 sound()를 반드시 구현해야 컴파일이 된다. 이렇게 공통 구조를 정의해 두면 코드의 중복을 줄이고 유지보수가 쉬워진다.
Animal dog = new Dog();
dog.breathe(); // 숨을 쉰다
dog.sound(); // 멍멍자동차 예제로 보는 자바 추상화
추상화 개념에 대해 조금 이해가 되었는가? 이어서 자동차라는 대상으로 예시를 들어 보겠다. 자동차에는 K5, K7, 그랜저, 아반떼처럼 다양한 브랜드와 모델이 존재한다. 하지만 대부분의 자동차는 엔진, 시동, 바퀴, 와이퍼 같은 공통 기능을 가지고 있다. 여기서는 추가 옵션 등은 제외하고 생각해 보자.
이러한 공통 기능을 아래와 같이 Car라는 추상 클래스로 정의하면 각 자동차 클래스는 이를 상속하여 구현할 수 있다.
abstract class Car {
abstract void startEngine();
abstract void stopEngine();
void changeWheel() {
System.out.println("바퀴를 교체한다");
}
}
class Avante extends Car {
@Override
void startEngine() { System.out.println("아반떼 시동"); }
@Override
void stopEngine() { System.out.println("아반떼 정지"); }
}
별거 아니다. 참 쉽다.
자바 추상화를 사용하는 이유
그렇다면 이 추상화를 왜 사용해야 할까? 가장 큰 이유는 유지보수와 코드 재사용성 때문이다. 예를 들어 자동차 시스템에 에어컨/히터라는 공통 기능이 추가되었다고 가정해 보자.
| 구분 | 수정 범위 |
|---|---|
| 추상화가 없다면 | 몇백 개나 될지 모르는 모든 자동차 클래스를 찾아 하나하나 수정해야 한다. |
| 추상화가 적용되어 있다면 | 추상 클래스 한 곳만 수정하면 모든 자식 클래스에 적용된다. |
개인의 소규모 프로젝트에서는 이런 수정이 그리 번거롭지 않을 수 있다. 하지만 필자가 확실하게 말할 수 있는 것은, 위에 소개한 자바 추상화 구조는 클래스가 많은 대규모 프로젝트에서 특히 큰 효과를 발휘한다는 점이다.
abstract class와 interface 차이
자바에서 추상화를 구현하는 방법은 크게 두 가지다. 추상 클래스(abstract class)와 인터페이스(interface)다. 둘 다 “무엇을 해야 하는지”를 정하는 도구이지만 쓰임이 조금 다르다.
| 구분 | abstract class | interface |
|---|---|---|
| 상속 키워드 | extends | implements |
| 다중 상속 | 하나만 상속 가능 | 여러 개 구현 가능 |
| 구현 코드 | 일반 메서드 구현 가능 | Java 8부터 default 메서드로 구현 가능 |
| 필드 | 일반 필드(상태) 가질 수 있음 | 상수(static final)만 가능 |
| 주로 쓰는 경우 | 공통 코드와 상태를 물려줄 때 | 역할(기능 명세)을 정할 때 |
interface Soundable {
void sound();
}
class Dog extends Animal implements Soundable {
@Override
public void sound() {
System.out.println("멍멍");
}
}
정리하면 “이 클래스는 무엇이다”를 표현할 때는 추상 클래스를, “이 클래스는 무엇을 할 수 있다”를 표현할 때는 인터페이스를 쓴다고 생각하면 이해하기 쉽다. 두 개념의 자세한 문법은 Oracle Java 튜토리얼의 추상 클래스 문서에서도 확인할 수 있다.
마무리
자바 추상화는 객체지향 프로그래밍에서 매우 중요한 개념이다. 공통적인 개념을 하나의 구조로 정의함으로써 코드의 중복을 줄이고 유지보수를 쉽게 만들 수 있다.
실무에서는 이러한 설계 구조가 프로젝트의 유지보수성과 개발 효율을 크게 좌우한다. 처음에는 조금 어려울 수 있지만 위와 같은 방식으로 공통 개념에 대한 구조를 잡는 것에 익숙해지도록 하자.
첫 댓글을 남겨보세요