디자인패턴을 공부하다 보니 어떨 땐 abstract 클래스로, 어떨 땐 interface로 구현을 하는 것을 보았는데
이 둘의 차이를 명확히 잘 몰라서 알아보았습니다.
Abstract Class와 Interface
추상클래스 (Abstract class)
자바에서는 하나 이상의 추상 메소드(abstract method)를 포함하는 클래스를 가리켜 추상 클래스(abstract class)라고 합니다.
여기서 추상 메소드란 자식 클래스에서 반드시 오버라이딩해야만 사용할 수 있는 메소드를 의미합니다.
자바에서 추상 메소드를 선언하여 사용하는 목적은 추상 메소드가 포함된 클래스를 상속받는 자식 클래스가 반드시 추상 메소드를 구현하도록 하기 위함입니다.
예를 들면 모듈처럼 중복되는 부분이나 공통적인 부분은 미리 다 만들어진 것을 사용하고, 이를 받아 사용하는 쪽에서는 자신에게 필요한 부분만을 재정의하여 사용함으로써 생산성이 향상되고 배포 등이 쉬워지기 때문입니다.
이러한 추상 메소드는 선언부만이 존재하며, 구현부는 작성되어 있지 않습니다.
바로 이 작성되어 있지 않은 구현부를 자식 클래스에서 오버라이딩하여 사용하는 것입니다.
// 추상 클래스 Animal 정의
abstract class Animal {
abstract void makeSound(); // 추상 메소드 makeSound 정의
}
// Animal 클래스를 상속받는 Dog 클래스 정의
class Dog extends Animal {
// 추상 메소드 makeSound를 구현
void makeSound() {
System.out.println("멍멍!");
}
}
// Animal 클래스를 상속받는 Cat 클래스 정의
class Cat extends Animal {
// 추상 메소드 makeSound를 구현
void makeSound() {
System.out.println("야옹!");
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog(); // Dog 객체 생성
Cat myCat = new Cat(); // Cat 객체 생성
myDog.makeSound(); // "멍멍!" 출력
myCat.makeSound(); // "야옹!" 출력
}
}
다시 추상 클래스로 돌아와서, 추상 클래스는 인스턴스화가 될 수 없으며 이를 인스턴스화하려고 하면 에러가 발생합니다.
추상 클래스의 메서드 중에서 일부는 구현이 있을 수 있고, 나머지는 추상 클래스로 남겨질 수 있습니다. 추상 클래스는 하위 클래스에서 반드시 구현되어야 합니다.
이러한 추상 클래스는 객체 지향 프로그래밍에서 중요한 특징인 다형성을 가지는 메소드의 집합을 정의할 수 있도록 해줍니다.
즉, 반드시 사용되어야 하는 메소드를 추상 클래스에 추상 메소드로 선언해 놓으면, 이 클래스를 상속받는 모든 클래스에서는 이 추상 메소드를 반드시 재정의해야 합니다.
Interface
- Interface는 객체 생성이 불가능하며, 클래스에서 구현해야 하는 일련의 메서드를 정의합니다.
- Interface의 메서드는 기본적으로 모두 추상 메소드이며, 구현이 없습니다.
- interface는 오로지 추상 메소드와 상수만을 포함할 수 있습니다.
// 인터페이스 Animal 정의
interface Animal {
void makeSound(); // 인터페이스 메소드 makeSound 정의
}
// Animal 인터페이스를 구현하는 Dog 클래스 정의
class Dog implements Animal {
// 인터페이스 메소드 makeSound를 구현
public void makeSound() {
System.out.println("멍멍!");
}
}
// Animal 인터페이스를 구현하는 Cat 클래스 정의
class Cat implements Animal {
// 인터페이스 메소드 makeSound를 구현
public void makeSound() {
System.out.println("야옹!");
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog(); // Dog 객체 생성
Cat myCat = new Cat(); // Cat 객체 생성
myDog.makeSound(); // "멍멍!" 출력
myCat.makeSound(); // "야옹!" 출력
}
}
하지만 java 8 이후로 default 키워드를 사용하여 구현이 있는 인터페이스 메소드를 만들 수 있습니다.
interface Drawable{
void draw();
default void msg(){System.out.println("default method");}
}
class Rectangle implements Drawable{
public void draw(){System.out.println("drawing rectangle");}
}
class TestInterfaceDefault{
public static void main(String args[]){
Drawable d=new Rectangle();
d.draw();
d.msg();
}
}
Output
drawing rectangle
default method
또한 Static으로도 선언 가능합니다.
interface Drawable{
void draw();
static int cube(int x){return x*x*x;}
}
class Rectangle implements Drawable{
public void draw(){System.out.println("drawing rectangle");}
}
class TestInterfaceStatic{
public static void main(String args[]){
Drawable d=new Rectangle();
d.draw();
System.out.println(Drawable.cube(3));
}
}
Output
drawing rectangle
27
둘의 차이점
접근 제어자 (Access Modifiers)
- 추상 클래스는 메서드와 속성에 대해 public, protected, private과 같은 접근 제어자를 가질 수 있습니다.
- 그러나 인터페이스의 메서드는 기본적으로 public이며, 다른 접근 제어자를 가질 수 없습니다.
상속 (Inheritance)
- 하나의 클래스는 하나의 추상 클래스만 상속 가능합니다.
- 하나의 클래스는 여러 인터페이스를 구현할 수 있습니다.
변수 (Variables)
- 추상 클래스는 멤버 변수를 가질 수 있습니다.
- 인터페이스는 멤버 변수를 가질 수 없습니다.
요약하면, 추상 클래스는 구체적인 서브클래스가 상속받을 기본 클래스를 제공하는 데 사용되며,
반면에 인터페이스는 클래스가 반드시 구현해야 하는 메서드 집합을 정의하는 데 사용됩니다.
추상 클래스는 구현된 메서드와 추상 메서드를 가질 수 있지만, 인터페이스는 오직 추상 메서드만을 가질 수 있습니다. 또한, 클래스는 하나의 추상 클래스만을 상속할 수 있지만, 여러 인터페이스를 동시에 구현할 수 있습니다.
추상 클래스의 특징
1. 직접 인스턴스화 불가능
- 추상 클래스는 직접적으로 객체를 생성할 수 없습니다. 다시 말해, 추상 클래스로부터 직접 객체를 만들 수는 없습니다.
2. 최소한 하나의 순수 가상 함수 포함
- 추상 클래스는 최소한 하나 이상의 순수 가상 함수(pure virtual function)를 포함해야 합니다.
- 순수 가상 함수는 구현이 없는 함수로, 해당 함수는 파생 클래스에서 반드시 구현되어야 합니다.
3. 추상 및 비추상 메서드 모두 포함 가능
- 추상 클래스는 추상 메서드뿐만 아니라 비추상 메서드도 포함할 수 있습니다. 비추상 메서드는 완전한 구현을 가지며 직접 호출될 수 있습니다.
4. 생성자와 소멸자 포함 가능
- 추상 클래스는 생성자와 소멸자를 포함할 수 있습니다. 이는 다른 클래스와 마찬가지로 객체의 초기화 및 정리를 위해 사용됩니다.
5. 멤버 변수 포함 가능
- 추상 클래스는 멤버 변수를 가질 수 있습니다. 이는 해당 클래스의 객체에 속하는 변수들을 말합니다.
6. 기본 클래스로 사용 가능
- 추상 클래스는 다른 클래스의 기본 클래스로 사용될 수 있습니다. 즉, 다른 클래스에서 상속받을 수 있습니다.
인터페이스의 특징
1. 메서드 및 속성 정의
- 인터페이스는 어떠한 클래스나 구조체가 해당 인터페이스를 구현할 때 반드시 가져야 하는 메서드와 속성들을 정의합니다.
2. 공통 프로토콜 제공
- 인터페이스는 서로 다른 소프트웨어 컴포넌트 간에 통신할 수 있는 공통 프로토콜을 제공합니다.
3. 다형성 지원
- 인터페이스는 다형성을 구현하는 데 사용될 수 있습니다. 다양한 클래스의 객체들이 동일한 타입으로 취급될 수 있게끔 하는 기능입니다.
4. 관심사 분리
- 인터페이스는 관심사를 분리함으로써, 소프트웨어 시스템의 여러 부분이 독립적으로 개발될 수 있도록 합니다.
5. 코드 재사용성 향상
- 인터페이스는 동일한 인터페이스를 구현하는 다양한 소프트웨어 컴포넌트가 동일한 코드 베이스를 재사용할 수 있게끔 합니다.
6. 디자인 패턴 강화
- 인터페이스는 특정 디자인 패턴을 강제함으로써, 특정 메서드나 속성이 반드시 구현되도록 할 수 있습니다. 예를 들면 어댑터 패턴(Adapter Pattern) 등이 있습니다.
7. 테스트 용이성 촉진
- 인터페이스는 테스트를 쉽게 할 수 있도록 도와줍니다. 인터페이스를 구현하는 가짜 객체(mock objects)를 사용하여 소프트웨어 컴포넌트를 독립적으로 테스트할 수 있습니다.
그래서, 언제 언제 사용해야 하는데?
(이 글을 작성하게 된 계기.. 각각 언제 사용해야 하는가입니다.)
추상 클래스를 사용해야 하는 경우
1. Java 애플리케이션에서 관련된 클래스들이 일부 코드를 공유해야 하는 상황이 있다면, 이 코드들을 추상 클래스 내에 넣고 이 추상 클래스를 관련 클래스들이 상속하도록 할 수 있습니다.
2. 추상 클래스 내에 비정적 또는 변경 가능한 필드를 정의하여, 이 필드들에 대한 상태를 수정하고 액세스 하는 메서드를 통해 해당 객체의 상태를 조작할 수 있습니다.
3. 추상 클래스를 상속하는 클래스들이 많은 공통 메서드 또는 필드를 가지거나, public 이외의 접근 제어자 (protected 및 private와 같은)가 필요한 경우 사용될 수 있습니다.
인터페이스를 사용해야 하는 경우
1. 전체적인 추상화가 필요하며, 인터페이스 내에서 선언된 모든 메서드는 해당 인터페이스를 구현하는 클래스에 의해 반드시 구현되어야 하는 경우 사용될 수 있습니다.
2. 클래스는 여러 인터페이스를 구현할 수 있는 다중 상속이 필요한 경우 사용될 수 있습니다.
3. 특정 데이터 유형의 동작을 지정하고, 해당 동작을 구현하는 클래스가 누구인지에 대한 고려가 필요하지 않은 경우 사용될 수 있습니다.
참고 자료
https://www.geeksforgeeks.org/difference-between-abstract-class-and-interface-in-java/
https://tcpschool.com/java/java_polymorphism_interface
'ComputerScience > Java' 카테고리의 다른 글
[Java] JUnit5 (0) | 2024.02.22 |
---|---|
[Java] Java에서 Queue와 구현체들 (+ ArrayList와 LinkedList의 차이) (0) | 2024.01.31 |
[Java] List와 Set의 차이, 그러면 Set은 어떻게 구현할까요? (0) | 2024.01.29 |
[Java] 인터페이스가 다중 상속이 가능한 이유? (0) | 2024.01.07 |
[Java] Garbage Collection (0) | 2023.09.17 |