[Java] 추상 클래스 vs 인터페이스

sue·2024년 4월 8일
0

JAVA

목록 보기
4/5

디자인 패턴 스터디를 진행하면서 다시 한 번 객체지향을 공부하게 됐다. 내용 중에 추상 클래스와 인터페이스가 나왔고 그 차이점에 대해서 스터디에서 이야기를 나눴었다. 그래서 정리할 겸 다시 공부한 내용을 적어본다...!

추상 클래스 (Abstract Class)

  1. 특징
  • 추상 클래스는 추상 메서드와 일반 메서드를 모두 가질 수 있음
  • 추상 메서드는 선언부만 있고 구현부가 없는 메서드
  • 일반 메서드는 구현부를 가지고 있어, 하위 클래스에서 공통적으로 사용할 수 있는 기능을 제공함
  • 추상 클래스를 상속받는 하위 클래스는 추상 메서드를 모두 구현해야 함
  • 추상 클래스는 멤버 변수를 가질 수 있음
  • 단일 상속만 지원. 클래스는 하나의 추상 클래스만 상속받을 수 있음
  1. 예제 코드
abstract class Animal {
    protected String name;

    public Animal(String name) {
        this.name = name;
    }

    public abstract void sound(); // 추상 메소드

    public void sleep() {
        System.out.println(name + "이(가) 잠을 잡니다.");
    }
}

class Dog extends Animal {
    public Dog(String name) {
        super(name);
    }

    public void sound() {
        System.out.println(name + "이(가) 멍멍 짖습니다.");
    }
}

class Cat extends Animal {
    public Cat(String name) {
        super(name);
    }

    public void sound() {
        System.out.println(name + "이(가) 야옹 웁니다.");
    }
}

public class AnimalExample {
    public static void main(String[] args) {
        Animal dog = new Dog("뽀삐");
        Animal cat = new Cat("나비");

        dog.sound();  // 출력: 뽀삐이(가) 멍멍 짖습니다.
        dog.sleep();  // 출력: 뽀삐이(가) 잠을 잡니다.

        cat.sound();  // 출력: 나비이(가) 야옹 웁니다.
        cat.sleep();  // 출력: 나비이(가) 잠을 잡니다.

        // 추상 클래스로 직접 객체 생성 시도 (컴파일 에러 발생)
        // Animal animal = new Animal("동물");
    }
}

인터페이스 (Interface)

  1. 특징
  • 인터페이스는 모든 메서드가 추상 메서드로 선언되며, 구현부를 가질 수 없음
  • 인터페이스를 구현하는 클래스는 인터페이스에 정의된 모든 메서드를 구현해야 함
    • 예외 : Java 8부터 인터페이스에 디폴트 메서드(default method)와 정적 메서드(static method)를 포함할 수 있게 됨
  • 인터페이스는 상수(static final)만 가질 수 있습니다.
  • 다중 상속을 지원. 클래스는 여러 인터페이스를 구현할 수 있음
  1. 예제 코드
interface Shape {
    double getArea();
    double getPerimeter();
}

class Circle implements Shape {
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    public double getArea() {
        return Math.PI * radius * radius;
    }

    public double getPerimeter() {
        return 2 * Math.PI * radius;
    }
}

class Rectangle implements Shape {
    private double width;
    private double height;

    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }

    public double getArea() {
        return width * height;
    }

    public double getPerimeter() {
        return 2 * (width + height);
    }
}

public class ShapeExample {
    public static void main(String[] args) {
        Shape circle = new Circle(5);
        Shape rectangle = new Rectangle(4, 6);

        System.out.println("원의 면적: " + circle.getArea());
        System.out.println("원의 둘레: " + circle.getPerimeter());

        System.out.println("사각형의 면적: " + rectangle.getArea());
        System.out.println("사각형의 둘레: " + rectangle.getPerimeter());

        // 인터페이스로 직접 객체 생성 시도 (컴파일 에러 발생)
        // Shape shape = new Shape();
    }
}
  • 인터페이스의 멤버변수는 상수만 가능함
    • 값 변경 시 컴파일 에러 발생
interface MyInterface {
    int MAX_VALUE = 100;  // public static final int MAX_VALUE = 100;
    String DEFAULT_NAME = "John";  // public static final String DEFAULT_NAME = "John";
}

class MyClass implements MyInterface {
    public void printValues() {
        System.out.println("최댓값: " + MAX_VALUE);
        System.out.println("기본 이름: " + DEFAULT_NAME);
        
        // 인터페이스의 멤버 변수는 값을 변경할 수 없습니다.
        // MAX_VALUE = 200;  // 컴파일 에러 발생
    }
}

public class InterfaceVariableExample {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.printValues();
        
        System.out.println("최댓값: " + MyInterface.MAX_VALUE);
        System.out.println("기본 이름: " + MyInterface.DEFAULT_NAME);
    }
}

실행결과
최댓값: 100
기본 이름: John
최댓값: 100
기본 이름: John
  • 디폴트 메서드(default method)와 정적 메서드(static method)를 포함한 인터페이스

    디폴트 메서드 (Default Method)

    • 인터페이스에서 기본 구현을 제공하는 메서드
    • 키워드 default를 사용하여 정의
    • 인터페이스를 구현하는 클래스에서 디폴트 메서드 오버라이드 가능
    • 디폴트 메서드를 사용하면 인터페이스에 새로운 메서드를 추가할 때 기존 구현 클래스에 영향을 주지 않고 기능을 추가 가능
      정적 메서드 (Static Method)
    • 인터페이스에서 정적 메서드를 정의 가능
    • 키워드 static을 사용하여 정의
    • 정적 메서드는 인터페이스 이름을 통해 직접 호출 가능
interface MyInterface {
    void abstractMethod();
    
    default void defaultMethod() {
        System.out.println("디폴트 메서드 호출");
    }
    
    static void staticMethod() {
        System.out.println("정적 메서드 호출");
    }
}

class MyClass implements MyInterface {
    public void abstractMethod() {
        System.out.println("추상 메서드 구현");
    }
}

public class InterfaceMethodExample {
    public static void main(String[] args) {
        MyClass obj = new MyClass();
        obj.abstractMethod();
        obj.defaultMethod();
        
        MyInterface.staticMethod(); // 직접 호출
    }
}

실행결과: 
추상 메서드 구현
디폴트 메서드 호출
정적 메서드 호출

추상 클래스와 인터페이스의 확연한 차이점을 추상 클래스는 공통 코드를 구현하고, 추상 메소드를 상속 받아서 직접 구현하여서 사용할 수 있다는 점이라고 생각했는데 인터페이스에서도 default 메소드를 이용하여 공통 코드를 구현할 수 있겠다 싶다.
확실한 차이는 다중 상속과 단일 상속인 것 같고, 추상 클래스는 is-a 관계, 인터페이스는 has-a 관계이므로 관계에 맞게 사용할 수 있게 고려하여 사용해야겠다.
또한 인터페이스는 주로 행동(behavior)을 정의하는 데 사용하고, 추상 클래스는 상태(state)와 행동(behavior)을 모두 가질 수 있다고 한다.
인터페이스는 주로 행동에 대한 규약을 정의하고, 다형성을 활용하기 위해 사용되는 반면
추상 클래스는 상속 계층 구조를 형성하고, 공통된 기능을 구현하여 코드 재사용성을 높이는 데 사용된다.
결국은 목적에 맞게 설계해서 사용하는 게 가장 중요하겠다 라는 게 오늘의 교훈!

profile
All is well ! 🔥

0개의 댓글