추상클래스(abstract class)와 인터페이스(interface)
객체지향 프로그래밍을 할 때 항상 추상클래스와 인터페이스 중에 어느 것을 선택해서 구현해야 되는 지에 대해 고민이 많았다. 어떠한 상황에서 추상클래스를 써야할 지, 또는 추상클래스 대신에 인터페이스를 쓰는 것이 더 나을 지에 대한 확신이 서지 않았다. 그럴 때마다 딜레마에 빠진 내가 검색을 통해서 차이를 비교한다음 추상클래스와 인터페이스를 선택해서 구현하는 상황이 너무 잦다보니 이 참에 두 형태의 의미와 차이를 비교해서 확실히 알아두려고 한다.
추상클래스(abstract class)란?
표면적인 의미 그 자체로 추상적으로 설계된 클래스이다. 일반적인 클래스가 가진 구성에 비해 많이 축소되고 간략한 형태를 가진 클래스라고 볼 수 있다. 대개 일반적인 상황에서 변수와 함수를 선언하는 정도에서만 그칠 수 있고 실제 기능은 구현되어 있지 않은 클래스이다. 왜 이러한 클래스가 필요할까? 추상클래스의 필요성에 대해서는 아래와 같은 이유가 있다.
1. 추상화
2. 코드의 표준화
3. 같은 이름 다른 기능 정의
- 추상화
추상화는 불필요한 기능을 없애고 공통적인 특성이나 기능을 간결하게 하나로 묶어 이름을 정의하는 것이라고 볼 수 있다. 추상화를 통해 불필요한 부분을 생략하고 객체의 속성 중 가장 중요한 것에만 중점을 두어 개략화하여 클래스를 설계하는 것이다. 추상클래스를 통해서 추상화를 실천할 수 있게 된다.
- 코드의 표준화
추상클래스를 통해 표준화를 실천하여 코드의 복잡성을 덜어내고 일관된 코드를 설계할 수 있다. 협업을 하는 데 있어 가장 중요한 것이 규칙을 준수하여 원하는 공동 목표를 이루어내는 것이다. 코드를 표준화하여 설계함으로써 협업하는데 혼란을 최소화할 수 있다. 추상클래스도 코드 표준화를 이루어내는 하나의 방법으로써 효율적인 협업을 실현하는 좋은 수단이 될 수 있다.
- 같은 함수 다른 기능 정의
같은 함수이지만 상속 받는 자식 클래스에 따라 다소 다른 기능을 구현하여 자식 클래스마다 수행하고자 하는 기능을 정의할 수 있다. 그렇기에 똑같은 부모 클래스를 상속받는 자식 클래스마다 이름은 같지만 다른 기능을 수행하는 함수를 가지고 있다.
본격적으로 추상클래스가 무엇인지 예시를 통해서 더 쉽게 알아보자.
public abstract class Parent {
public int abs_a;
public int abs_b = 200;
final int abs_c = 300;
abstract void abs_method();
void method() {
System.out.println("This is normal Method");
}
static void s_method() {
System.out.println("This is static Method");
}
}
추상클래스는 멤버 변수를 정의할 수 있고, 추상메소드 선언과 일반메소드의 정의 모두 가능하다. 일반적인 클래스와는 다르게 추상화라는 주된 목적을 실현하기 위해 등장한 클래스이다. 추상메소드 선언이 가능하다는 특성을 통해 이를 상속받는 어느 자식 클래스에서도 추상메소드를 Overriding하여 각 자식클래스가 원하는 기능을 구현할 수 있게 된다.
추상클래스를 상속받는 자식 클래스를 살펴보며 추상클래스의 특성을 자세하게 살펴보자.
public class Child2 extends Parent{
@Override
void abs_method() {
System.out.println("This abs_method called from Child2");
abs_a = 100;
System.out.println("abs_a : " + abs_a);
System.out.println("abs_c : " + abs_c);
s_method();
}
@Override
void method() {
super.method();
System.out.println("This method called from Child2");
System.out.println("abs_b : " + abs_b);
System.out.println("abs_c : " + abs_c);
s_method();
}
}
추상클래스를 상속받는 자식 클래스의 코드를 살펴보면 추상메소드의 기능을 정의하거나 일반메소드의 기능을 추가 혹은 수정을 위한 재정의가 가능한 것을 볼 수 있다. 또한 추상클래스의 멤버 변수에 접근 가능하여 이를 호출하거나 값을 변경하는 등 자식 클래스가 원하는대로 추상클래스의 멤버 변수를 활용할 수 있다. 마찬가지로 추상클래스에서 정의한 상수도 자식 클래스에서 접근 가능하여 호출을 통해 활용이 가능하다.
인터페이스(interface)란?
인터페이스는 사전적 의미로는 객체와 객체 사이의 상호작용을 위한 매개체, 또는 하나의 시스템에서 2개의 구성 요소가 상호작용할 수 있도록 접속되는 경계로 정의할 수 있다. 하지만 SW에서 정의한 인터페이스는 동일한 목적 하에 동일한 기능을 정의하도록 클래스에 강제하는 것이라고 표현할 수 있다. 긍정적인 표현으로는 클래스가 구현해야 할 요소를 정해주어 인터페이스에서 선언한 추상적 기능을 잊지 않고 모두 구현할 수 있게 해주는 역할도 수행해준다고 볼 수 있다. 추상클래스의 사용 목적과 동일하게 추상화 및 코드의 표준화 등을 통해 원활한 협업과 소스 코드의 일관성을 유지하는 데 효과적으로 사용할 수 있다.
원래 인터페이스는 추상클래스가 가지고 있는 특성보다 제한적인 특성을 가지고 있다. 추상클래스와 다르게 상수 정의 및 추상메소드 선언 정도만 가능하다는 특징을 가지고 있다. 하지만 Java 8에서 몇 가지 기능이 개선됨에 따라 인터페이스의 제한사항이 일부 개선되었다.
일단 예제 코드를 통해 인터페이스의 본래 주요 특성을 살펴보자.
public interface IParent {
int num1 = 1000;
int num2 = 2000;
void absMethod();
}
인터페이스는 추상클래스와 다르게 추상메소드에 'abstarct' 키워드를 붙이지 않는다. 붙이지 않아도 인터페이스 안에서 선언하는 함수는 추상메소드로 인식한다. 이러한 점을 볼 때 추상클래스에 비해서 덜 신경써도 되는 부분이라고 할 수 있을 것 같다.
그리고 인터페이스에서는 추상클래스와 달리 멤버 변수 선언과 접근제어자 설정이 불가하다. 상수 정의만 가능하다는 점에서 추상클래스가 제공하는 기능에 비해서는 다소 제한적인 느낌을 받을 수 있다. 그래도 다행스럽게도(?) static 메소드 정도는 정의할 수 있어 이를 참조하는 클래스에서는 static 메소드에 접근 가능하다.
추상클래스에 비해서 다소 제한사항이 있음에도 불구하고 인터페이스만의 매력적인 장점을 가지고 있는데 아래의 예제 코드를 통해 확인해보자.
public interface IParent2 {
int num3 = 3000;
int num4 = 4000;
void anotherMethod();
}
위와 같은 또 다른 인터페이스를 선언하였다. 2개의 인터페이스를 정의했는데 과연 클래스에서는 이 2개의 인터페이스 모두 참조할 수 있을까?
public class Child implements IParent, IParent2 {
@Override
public void absMethod() {
System.out.println("This method is absMethod");
System.out.println("num1 = " + num1);
System.out.println("num3 = " + num3);
}
@Override
public void anotherMethod() {
System.out.println("This method is anotherMethod");
System.out.println("num2 = " + num2);
System.out.println("num4 = " + num4);
}
}
놀랍게도 클래스가 2개의 인터페이스 모두 참조 가능하여 두 인터페이스의 모든 추상메소드의 기능을 정의할 수 있다. 게다가 특정 인터페이스에서 정의한 static 메소드라도 이를 참조하여 구현하는 클래스에서는 어디에서든지 호출이 가능하다. 상수도 마찬가지로 클래스의 어디에서든 호출이 가능하다.
추상클래스는 자식클래스에서 단 하나의 추상클래스를 상속받을 수 밖에 없는 특성을 가지고 있었지만 인터페이스는 이러한 추상클래스의 한계를 극복한 형태라고 볼 수 있다. 그렇기에 클래스에서 여러 인터페이스의 특성을 가지고 있다면 주저없이 공통된 특성을 가지고 있는 모든 인터페이스를 참조할 수 있다.
여기까지가 일반적인 인터페이스의 특성이라고 볼 수 있다. 앞서 잠깐 언급하였듯이 Java 8에서 'default' 키워드를 통한 일반 메소드 정의가 가능해졌고, static 메소드도 정의가 가능해지면서 인터페이스를 보다 유용하게 사용 가능해졌다. 예시를 통해 살펴보자.
public interface IParent3 {
default void normalIMethod() {
System.out.println("default method in Interface!");
}
static void ISMethod() {
System.out.println("This is static Method in Interface");
}
}
이처럼 Java 8에서 'default'와 'static'을 통해 인터페이스에서 일반 메소드와 static 메소드를 정의할 수 있어 이전의 인터페이스 한계를 일부 극복했다. (이와 같은 여러 큰 변화가 Java 8에서 이루어지다보니 이래서 대부분의 국내 기업 혹은 기관에서 최소 Java 8이상부터 사용하는 것 같다.)
public class Child implements IParent, IParent2, IParent3 {
@Override
public void absMethod() {
System.out.println("This method is absMethod");
System.out.println("num1 = " + num1);
System.out.println("num3 = " + num3);
IParent3.ISMethod();
normalIMethod();
}
@Override
public void anotherMethod() {
System.out.println("This method is anotherMethod");
System.out.println("num2 = " + num2);
System.out.println("num4 = " + num4);
IParent3.ISMethod();
normalIMethod();
}
@Override
public void normalIMethod() {
IParent3.super.normalIMethod();
System.out.println("This method called from Child");
}
}
이처럼 자식 클래스에서 인터페이스를 상속받게 되면, 인터페이스에서 정의한 일반 메소드를 다른 메소드에서 호출하거나, 클래스 내에서 Overriding하여 기능을 수정하거나 더 추가하여 구현할 수 있다. 또한 인터페이스에서 정의한 static 메소드를 호출 가능하여 자식 클래스 내 원하는 곳에서 적절히 활용 가능하다.
추상클래스와 인터페이스의 차이
추상클래스 | 인터페이스 | |
접근제어자 | 가능 | 'public', 'default'만 가능 |
상수 정의 | 가능 | 가능 |
멤버 변수 | 가능 | 불가능 |
추상 메소드 | 가능 | 가능('abstract' 키워드 생략 가능) |
일반 메소드 | 가능 | 가능('default' 키워드 필요) |
다중 상속 | 불가능 | 가능 |
추상클래스와 인터페이스의 차이를 알아보기 쉽게 표로 정리하였다. 개발할 때 추상클래스와 인터페이스의 모호함으로 인해 이를 적절하게 활용하여 코드를 작성하는 것이 어려웠다. 하지만 이번 계기를 통해서 추상클래스와 인터페이스에 대해 자세히 알아보게 되었고, 이러한 차이로 인해 필요한 상황에서 적절하게 선택하여 구현할 수 있겠다는 확신이 들게 되었다. 앞으로는 무조건 추상클래스만 활용하거나 인터페이스만 활용하는 것이 아닌 적당한 상황에서 추상클래스와 인터페이스를 적절하게 활용하는 것을 실천하여 소스 코드의 일관성과 간결함을 유지하는데 노력해야겠다.
- 참고
https://aileen93.tistory.com/107
[JAVA] 추상(abstract) 클래스와 인터페이스(interface) 클래스
추상(abstract) 클래스와 인터페이스(interface) 클래스 1. 추상 클래스와 추상 메소드란? 추상클래스란 말 그대로 추상적으로 밖에 그려지지 않은 클래스라고 한다. 즉, 클래스가 전체적인 구성을
aileen93.tistory.com
https://sungwoon.tistory.com/58
[Java-자바]추상 클래스 및 추상 메서드(abstract class and abstract method)
추상메서드란 "추상" 사전적 의미로 "여러가지 사물이나 개념에 공통되는 특성이나 속성따위를 추출하여 파악하는 작용"이라는 의미이다. 추상화 : 클래스간의 공통점을 찾아내서 공통의 부모
sungwoon.tistory.com
https://velog.io/@codemcd/인터페이스Interface
[JAVA] 인터페이스(Interface)
인터페이스란? 인터페이스의 사전적 의미는 다음과 같다. > 하나의 시스템을 구성하는 2개의 구성 요소(하드웨어, 소프트웨어) 또는 2개의 시스템이 상호작용할 수 있도록 접속되는 경계(boundary),
velog.io
https://codingwell.tistory.com/73
[Java] 인터페이스(interface)
인터페이스란? 인터페이스는 객체와 객체 사이에서 상호작용의 매개로 쓰이는데, 일종의 추상클래스이다. 그러나 추상클래스보다 추상화 정도가 높아서 일반 메소드나 멤버변수를 구성원으로
codingwell.tistory.com
https://math-coding.tistory.com/169
[Java] 자바 인터페이스(Interface) 사용
이 글은 "자바 온라인 스터디" 내용을 공부하여 작성하였습니다. 인터페이스(Interface)란? 현업에서 소스코드 작성 시 클래스를 처음부터 구현하게 된다면 코드의 가독성도 떨어지고 시간도 오래
math-coding.tistory.com
'기본기 다지기 > 문법' 카테고리의 다른 글
enum & struct (0) | 2022.06.12 |
---|---|
Thread 실습 - Java (0) | 2022.04.16 |
Thread란? (0) | 2022.04.03 |
static 이란? (0) | 2022.03.13 |