오늘은 추상 클래스와 인터페이스에 대해 각각의 특징과 공통점 그리고 차이점, 이 기능들이 갖는 의미를 이해해보겠습니다.
추상 클래스 (Abstract class)
다른 클래스들이 상속받아 사용할 수 있는 특별한 형태의 클래스로, 공통된 기능을 하나의 클래스에 모아두어 코드의 재사용성을 높일 수 있다. 추상 클래스의 하위 클래스는 추상 클래스의 속성과 매서드를 사용할 수 있어, 코드를 재작성하지 않아도 된다. 또한 추상 클래스의 경우 직접 인스턴스화 할 수 없으며, 구현을 포함하는 메서드( Concrete Methods) 와 그렇지 않은 메서드를 모두 가질 수 있다. ( 추상 메서드의 경우 반드시 서브 클래스에서 구현 필요 )
➡︎ 상속을 통한 코드의 재사용성 증가
일반 클래스와 다른 점
그럼 일반 클래스랑은 뭐가 다른거지 ?
일반 클래스의 경우 모든 메서드가 구현되어있어야하고, 인스턴스 생성이 가능합니다. 또한 추상 클래스는 반드시 다른 클래스가 상속받아야하며, 상속 받는 클래스는 추상 메서드를 구현해야 합니다. ( 일반 메서드는 상속으로부터 자유로움)
인터페이스 (Interface)
클래스가 구현해야만 하는 메서드의 집합을 정의하며 이는 인터페이스가 구현한 클래스가 어떤 동작을 수행할 것인지에 대한 약속을 명확히 하는 계약과도 같다. 추상 클래스의 경우 상속을 통한 코드의 재사용성 향상이 목적이라면, 인터페이스의 경우에는 디커플링에 그 초점이 맞추어져 있다. 인터페이스는 일련의 프로토콜 또는 계약을 추상화한 것으로, 설계 시 구현 방법에 종속되지 않아야 하며, 도메인 개념적인 수준에서 진행되어야만 한다. 이는 구현을 분리시킴으로써 코드의 결합도를 줄이고 확장성을 높일 수 있다.
➡︎ 디커플링을 통한 추상화, 코드의 확장성 유연성 증가
앞서 정리했듯이, 인터페이스와 추상 클래스는 그 존재 목적이 다릅니다.
가장 중요한 존재 목적의 차이를 알았으니, 이를 기반으로 세부적인 특징을 정리해보겠습니다.
주요 특징
1. 구현의 강제성
인터페이스의 경우 구현하는 클래스에서 정의된 메서드가 반드시 구현되어야만 합니다. ( 기본 메서드 제외 )
반면 추상 클래스의 경우 추상 클래스 내에서 구현이 가능하며, 구현한 메서드에 대해 하위 클래스에서의 오버라이드 또한 가능합니다
abstract class Animal {
void sleep() {
System.out.println("Sleeping...");
}
abstract void eat(); //추상 메서드 -> 반드시 상속받은 클래스에서 구현
}
+인터페이스의 기본 메서드
public interface MyInterface {
void existingMethod();
default void newMethod() {
System.out.println("This is a new default method.");
}
}
2. 단일 vs 다중 상속
인터페이스의 경우 다중 상속이 가능하여, 한 클래스에서 여러 개의 인터페이스를 상속 받을 수 있습니다.
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("오리날다");
}
@Override
public void swim() {
System.out.println("오리헤엄");
}
}
반면 추상 클래스는 단일 상속으로 하나의 추상 클래스에서만 상속 받을 수 있습니다.
public abstract class Animal {
public abstract void makeSound();
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("왈왈");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("야옹");
}
}
3. 필드(field) 속성
인터페이스의 경우 public static final ( 상수 ) 속성만 가질 수 있습니다.
public interface MyInterface {
int MY_CONSTANT = 10; // public static final int MY_CONSTANT = 10;
}
반면 추상 클래스는 static, final이 아닐 수 있고 public, protected, private인 속성으로 선언할 수 있습니다.
public abstract class AbstractClass {
public int publicField;
public void publicMethod() {
System.out.println("public method.");
}
protected int protectedField;
protected void protectedMethod() {
System.out.println("protected method.");
}
private int privateField;
private void privateMethod() {
System.out.println("private method.");
}
// 추상 메서드 (접근 제어자는 public 또는 protected만 사용 가능)
public abstract void abstractMethod();
// protected 추상 메서드
protected abstract void protectedAbstractMethod();
}
public class ConcreteClass extends AbstractClass {
@Override
public void abstractMethod() {
System.out.println("Implemented abstract method.");
}
@Override
protected void protectedAbstractMethod() {
System.out.println("Implemented protected abstract method.");
}
public void accessFieldsAndMethods() {
// public 멤버 접근
publicField = 10;
publicMethod();
// protected 멤버 접근
protectedField = 20;
protectedMethod();
// private 멤버는 직접 접근 불가 (동일 클래스 내에서만 접근 가능)
// privateField = 30; // 컴파일 에러
// privateMethod(); // 컴파일 에러
}
}
그래서 추상 클래스와 인터페이스를 언제 사용할까?
- 추상클래스나 인터페이스와 상속하는 클래스와의 관계 (is-a or has-a) 확인하기-> is-a 관계는 추상 클래스, has-a 관계는 인터페이스
- 해결하려는 문제가 코드의 재사용성인지, 추상화인지 파악하기->코드의 재사용성은 추상 클래스, 디커플링은 인터페이스
- 설계 방식이 상향식인지 하향식인지 파악하기->상향식은 추상 클래스, 하향식은 인터페이스
해당 게시글은 망나니 개발자 선생님의 글을 참고하였습니다.
'Java' 카테고리의 다른 글
객체지향의 사실과 오해를 읽고 (1) | 2025.02.12 |
---|---|
VO(Value Object)는 무엇이고 왜 쓰는 걸까? (1) | 2025.01.23 |
자바의 런타임 데이터 영역에 대하여 (2) | 2024.06.15 |
자바의 실행과정과 JIT에 대하여 (0) | 2024.06.12 |
JAVA를 잡아보자 (프로그래밍 언어, 자바) (2) | 2024.01.21 |
오늘은 추상 클래스와 인터페이스에 대해 각각의 특징과 공통점 그리고 차이점, 이 기능들이 갖는 의미를 이해해보겠습니다.
추상 클래스 (Abstract class)
다른 클래스들이 상속받아 사용할 수 있는 특별한 형태의 클래스로, 공통된 기능을 하나의 클래스에 모아두어 코드의 재사용성을 높일 수 있다. 추상 클래스의 하위 클래스는 추상 클래스의 속성과 매서드를 사용할 수 있어, 코드를 재작성하지 않아도 된다. 또한 추상 클래스의 경우 직접 인스턴스화 할 수 없으며, 구현을 포함하는 메서드( Concrete Methods) 와 그렇지 않은 메서드를 모두 가질 수 있다. ( 추상 메서드의 경우 반드시 서브 클래스에서 구현 필요 )
➡︎ 상속을 통한 코드의 재사용성 증가
일반 클래스와 다른 점
그럼 일반 클래스랑은 뭐가 다른거지 ?
일반 클래스의 경우 모든 메서드가 구현되어있어야하고, 인스턴스 생성이 가능합니다. 또한 추상 클래스는 반드시 다른 클래스가 상속받아야하며, 상속 받는 클래스는 추상 메서드를 구현해야 합니다. ( 일반 메서드는 상속으로부터 자유로움)
인터페이스 (Interface)
클래스가 구현해야만 하는 메서드의 집합을 정의하며 이는 인터페이스가 구현한 클래스가 어떤 동작을 수행할 것인지에 대한 약속을 명확히 하는 계약과도 같다. 추상 클래스의 경우 상속을 통한 코드의 재사용성 향상이 목적이라면, 인터페이스의 경우에는 디커플링에 그 초점이 맞추어져 있다. 인터페이스는 일련의 프로토콜 또는 계약을 추상화한 것으로, 설계 시 구현 방법에 종속되지 않아야 하며, 도메인 개념적인 수준에서 진행되어야만 한다. 이는 구현을 분리시킴으로써 코드의 결합도를 줄이고 확장성을 높일 수 있다.
➡︎ 디커플링을 통한 추상화, 코드의 확장성 유연성 증가
앞서 정리했듯이, 인터페이스와 추상 클래스는 그 존재 목적이 다릅니다.
가장 중요한 존재 목적의 차이를 알았으니, 이를 기반으로 세부적인 특징을 정리해보겠습니다.
주요 특징
1. 구현의 강제성
인터페이스의 경우 구현하는 클래스에서 정의된 메서드가 반드시 구현되어야만 합니다. ( 기본 메서드 제외 )
반면 추상 클래스의 경우 추상 클래스 내에서 구현이 가능하며, 구현한 메서드에 대해 하위 클래스에서의 오버라이드 또한 가능합니다
abstract class Animal {
void sleep() {
System.out.println("Sleeping...");
}
abstract void eat(); //추상 메서드 -> 반드시 상속받은 클래스에서 구현
}
+인터페이스의 기본 메서드
public interface MyInterface {
void existingMethod();
default void newMethod() {
System.out.println("This is a new default method.");
}
}
2. 단일 vs 다중 상속
인터페이스의 경우 다중 상속이 가능하여, 한 클래스에서 여러 개의 인터페이스를 상속 받을 수 있습니다.
public interface Flyable {
void fly();
}
public interface Swimmable {
void swim();
}
public class Duck implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("오리날다");
}
@Override
public void swim() {
System.out.println("오리헤엄");
}
}
반면 추상 클래스는 단일 상속으로 하나의 추상 클래스에서만 상속 받을 수 있습니다.
public abstract class Animal {
public abstract void makeSound();
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("왈왈");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("야옹");
}
}
3. 필드(field) 속성
인터페이스의 경우 public static final ( 상수 ) 속성만 가질 수 있습니다.
public interface MyInterface {
int MY_CONSTANT = 10; // public static final int MY_CONSTANT = 10;
}
반면 추상 클래스는 static, final이 아닐 수 있고 public, protected, private인 속성으로 선언할 수 있습니다.
public abstract class AbstractClass {
public int publicField;
public void publicMethod() {
System.out.println("public method.");
}
protected int protectedField;
protected void protectedMethod() {
System.out.println("protected method.");
}
private int privateField;
private void privateMethod() {
System.out.println("private method.");
}
// 추상 메서드 (접근 제어자는 public 또는 protected만 사용 가능)
public abstract void abstractMethod();
// protected 추상 메서드
protected abstract void protectedAbstractMethod();
}
public class ConcreteClass extends AbstractClass {
@Override
public void abstractMethod() {
System.out.println("Implemented abstract method.");
}
@Override
protected void protectedAbstractMethod() {
System.out.println("Implemented protected abstract method.");
}
public void accessFieldsAndMethods() {
// public 멤버 접근
publicField = 10;
publicMethod();
// protected 멤버 접근
protectedField = 20;
protectedMethod();
// private 멤버는 직접 접근 불가 (동일 클래스 내에서만 접근 가능)
// privateField = 30; // 컴파일 에러
// privateMethod(); // 컴파일 에러
}
}
그래서 추상 클래스와 인터페이스를 언제 사용할까?
- 추상클래스나 인터페이스와 상속하는 클래스와의 관계 (is-a or has-a) 확인하기-> is-a 관계는 추상 클래스, has-a 관계는 인터페이스
- 해결하려는 문제가 코드의 재사용성인지, 추상화인지 파악하기->코드의 재사용성은 추상 클래스, 디커플링은 인터페이스
- 설계 방식이 상향식인지 하향식인지 파악하기->상향식은 추상 클래스, 하향식은 인터페이스
해당 게시글은 망나니 개발자 선생님의 글을 참고하였습니다.
'Java' 카테고리의 다른 글
객체지향의 사실과 오해를 읽고 (1) | 2025.02.12 |
---|---|
VO(Value Object)는 무엇이고 왜 쓰는 걸까? (1) | 2025.01.23 |
자바의 런타임 데이터 영역에 대하여 (2) | 2024.06.15 |
자바의 실행과정과 JIT에 대하여 (0) | 2024.06.12 |
JAVA를 잡아보자 (프로그래밍 언어, 자바) (2) | 2024.01.21 |