개요
오늘은 자바에서 자주 사용되는 추상 클래스와 인터페이스의 특징과 차이에 대해 정리해 보자.
추상 클래스
자바에서 추상 클래스란 하나 이상의 추상 메서드를 가지고 있는 클래스를 의미한다.
추상 메서드란, 구현부가 작성되지 않은 메서드를 의미한다. 이 메서드를 사용하기 위해선 해당 추상 클래스를 상속받는 하위 클래스에서 반드시 재정의해야 한다.
추상 클래스와 추상 메서드는 코드 상에 abstact라는 키워드를 명시해야 한다.
아래의 클래스는 Animal이라는 추상 클래스가 있고, 클래스 내부엔 makeSound()라는 추상 메서드가 있다.
abstract class Animal {
String name;
int age;
abstract void makeSound();
}
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("냐옹~");
}
}
Animal을 상속받은 Cat은 makeSound() 메서드를 재정의(Override)할 수 있다.
물론 위의 Animal 클래스는 추상 메서드만 가지고 있지만, 멤버 변수를 가질 수도 있고, 구현부가 작성된 일반 메서드 또한 사용 가능하다.
그리고 추상 클래스 내부에선 멤버 변수, 추상 메서드 모두 접근 제어자를 자유롭게 사용할 수 있다.
하지만 다중 상속이 불가능한 단점이 있다.
인터페이스
인터페이스란 여러 클래스에서 사용되는 공통된 메서드들을 정의한 것이다.
인터페이스의 내부에서도 일반 메서드(default, static)를 선언할 수 있다. 하지만 멤버 변수는 오로지 상수만 허용된다.
인터페이스를 사용하는 방법은 추상 클래스와 비슷하게, 인터페이스를 구현하는 방식으로 동작한다.
인터페이스에선 ‘인터페이스를 하위 클래스가 상속받는다’고 표현하는 것보단, ‘하위 클래스가 인터페이스를 구현한다’라고 보는 것이 더 적합할 듯하다. → 이 부분에서 추상 클래스와 인터페이스의 목적 차이가 드러남!
interface Animal {
int LEGS = 4; // 상수
void makeSound();
}
class Dog implements Animal {
@Override
public void makeSound() {
System.out.println("멍멍!");
}
}
참고로 인터페이스를 정의할 때, 접근 제어자와 키워드를 따로 설정해주지 않아도 컴파일러가 알아서 인터페이스의 멤버 변수에는 public static final을, 메서드에는 public abstract를 붙여준다. (default 메서드 제외)
또한 인터페이스의 장점은 다중 상속을 지원한다.
interface Animal {
void makeSound();
}
interface Pet {
void play();
}
class Dog implements Animal, Pet {
@Override
public void makeSound() {
System.out.println("멍멍!");
}
@Override
public void play() {
System.out.println("강아지가 놉니다!");
}
}
추상 클래스 vs 인터페이스
위에서 정리한 글을 봤을 때, 두 기술 모두 각각의 장・단점이 확실하다.
기능적으로 봤을 땐, 두 기술의 차이는 다음과 같다.
추상 클래스 | 인터페이스 | |
멤버 변수 | 상수, 일반 변수 모두 가능 | 상수만 가능!! |
메서드 | 일반 메서드, 추상 메서드 모두 사용 가능 | 일반 메서드, 추상 메서드 모두 사용 가능 하지만 일반 메서드는 default나 static 제어자가 필요함 |
상속 | 단일 상속만 가능! | 단일 상속, 다중 상속 모두 가능! |
접근 제어자 | public, protected, private 모두 사용 가능 | public만 사용 가능 (default, static 예외) 만약 public을 작성하지 않은 경우 컴파일러가 알아서 public으로 설정 |
사용 목적 | 하위 클래스는 추상 클래스를 상속받음 | 구현체는 인터페이스를 구현함 |
관계 | 추상 클래스와 하위 클래스는 강하게 결합됨 | 인터페이스와 구현체는 추상 클래스보단 비교적 약하게 결합됨 |
추상 클래스 + 인터페이스
물론 이 둘을 함께 사용하는 방법도 있다.
추상 클래스는 공통된 멤버 변수나 메서드, 인터페이스는 다중 상속을 지원한다.
이 두 장점을 합치면 더 유연하고 깔끔한 코드를 작성할 수 있다.
interface Animal {
void makeSound();
void play();
}
public abstract class Mammal implements Animal {
protected String name;
protected int age;
public Mammal(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void play() {
System.out.println(name + "이 놉니다.");
}
}
public class Dolphin extends Mammal {
public Dolphin(String name, int age) {
super(name, age);
}
@Override
public void makeSound() {
System.out.println("쁘리리릭!");
}
}
이처럼 play() 기능은 추상 클래스에서 선언하여 반복된 코드 작성을 막을 수 있고, makeSound() 기능은 인터페이스에서 정의하여 각 하위 클래스에서 자신만의 방식으로 구현할 수 있다.
이렇게 함으로써, 코드의 재사용성을 높이고, 다형성을 활용할 수 있게 되었다.
'개발 일기' 카테고리의 다른 글
[개발 일기] 2025.02.06 - 초기화하다? (1) | 2025.02.06 |
---|---|
[개발 일기] 2025.02.05 - 식별 관계, 비식별 관계 (0) | 2025.02.05 |
[개발 일기] 2025.02.03 - bit, byte (Feat : byte의 혼란) (0) | 2025.02.03 |
[개발 일기] 2025.02.02 - 제네릭 (0) | 2025.02.02 |
[개발 일기] 2025.02.01 - 객체 지향 생활 체조 9가지 규칙 (2) (1) | 2025.02.01 |