추상클래스와 인터페이스

추상 메소드(Abstract Method)

public abstract void abstractMethod(); 
  • abstract 키워드와 함께 원형만 선언되고, 코드는 작성되지 않는 메서드

  • abstractfinal 키워드를 동시에 표기할 수 없음

추상 클래스

abstract class 클래스이름 { // 추상 클래스
    void 메서드1() {...}
    abstract public void 메서드2(); // 추상 메서드
}
  • 개념

    • 추상 메서드를 최소 한 개 이상 가지고 abstract로 선언된 클래스

      • 최소 한 개 이상의 추상 메서드를 포함하는 경우, 반드시 추상 클래스로 선언해야 함

    • 추상 메서드가 없어도 abstract로 선언한 클래스

      • 추상 메서드가 하나도 없는 경우라도 추상 클래스로 선언할 수 있음

  • 구현

    • 서브 클래스에서 슈퍼 클래스의 모든 추상 메서드를 오버라이딩하여 실행 가능한 코드로 구현

  • 목적

    • 객체(인스턴스)를 생성하기 위함이 아니며, 상속을 위한 부모 클래스로 활용하기 위한 것

    • 여러 클래스들의 공통된 부분을 추상화하여 상속 받는 클래스에게 구현을 강제화하기 위한 것

      • 메서드의 동작을 구현하는 자식 클래스로 책임을 위임

    • 추상 클래스의 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하기 위함

  • 특징

    • 추상 메소드뿐만 아니라 생성자, 필드, 일반 메소드도 포함할 수 있음

    • 선언만 하기 때문에 인스턴스로 생성될 수 없음

인터페이스(interface)

interface 인터페이스이름 {
    public static final String name = "defaultName";

    public abstract void setName(String name);

    default void print() { System.out.println("Hello World"); }
}
  • 개념

    • 추상 메소드와 상수만을 포함하며, interface 키워드를 사용하여 선언

  • 구현

    • 인터페이스를 상속받고, 추상 메서드를 모두 구현한 클래스를 구현

    • implements 키워드 사용

  • 목적

    • 상속받을 서브 클래스에게 구현할 메서드들의 원형을 모두 알려주어, 클래스가 자신의 목적에 맞게 메서드를 구현하도록 하는 것

    • 구현 객체의 같은 동작을 보장하기 위한 목적

    • 서로 관련이 없는 클래스에서 공통적으로 사용하는 방식이 필요하지만, 기능을 각각 구현할 필요가 있는 경우에 사용

  • 특징

    • 인터페이스는 상수 필드와 추상 메서드만으로 구성됨

    • .java 형태의 소스 파일로 작성되고 .class 형태로 컴파일 되기에 물리적 형태는 클래스와 동일함

    • 모든 메서드는 추상 메서드로서, public abstract 속성이며 생략 가능함

    • 상수는 public static final 속성이며, 생략 가능

    • 클래스에는 다중 구현을 지원하고, 인터페이스끼리는 다중 상속을 지원함

    • 자바 8부터는 defaultstatic 키워드를 이용하여 일반 메소드처럼 코드를 작성할 수 있음(구현 클래스에 강제성 x)

추상 클래스와 인터페이스

추상 클래스인터페이스

사용 키워드

abstract

interface

사용 가능 변수

제한 없음

static final(상수)

사용 가능 접근 제어자

제한 없음

public

상속 키워드

extends

implements

다중 상속 여부

불가능

가능

  • 공통점

    • 인스턴스(객체)는 생성할 수 없음(new 생성자 사용 x, 구현체 사용해야 함)

    • 선언만 되어있고 구현 내용이 없음

    • 인터페이스와 추상 클래스의 구현 클래스는 추상 메서드를 반드시 구현해야 함(강제)

  • 차이점

    • 목적 차이

      • 추상 클래스는 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하기 위해 사용

      • 인터페이스는 구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용

    • 추상 클래스는 클래스이지만, 인터페이스는 클래스가 아님

    • 추상 클래스는 단일 상속이지만, 인터페이스는 다중 상속 가능

    • 추상 클래스는 is a kind of, 인터페이스는 can do this

      • 추상 클래스: Appliances(Abstract Class) - TV, Refrigerator

      • 인터페이스: Flyable(Interface) - Plane, Bird

다중 상속과 단일 상속 (클래스는 왜 단일 상속만 지원하나요?)

  • 클래스는 단일 상속만 지원하고, 인터페이스는 다중 상속을 지원한다.

  • 만약 클래스에서 다중 상속이 가능하다면 메서드 출처의 모호성 등 여러 문제가 발생할 수 있다.

  • 클래스 A와 클래스 B에 한 메서드가 동일한 시그니처를 가질 경우, 자식 클래스는 둘 중 어느 것을 상속받아야 할지 파악할 수 없게 되는 문제가 생긴다.

  • 관계가 다이아몬드 형태라 하여 다이아몬드 문제라고 한다.

  • 인터페이스는 실질적인 구현이 이루어져 있지 않고 추상 메서드만 가지고 있기 때문에, 메서드가 겹치더라도 최종 구현 부분은 구현 클래스에서 새롭게 정의해야 하기 때문에 문제가 없다.

클래스의 경우

public class GranFar {
    void say() {
        System.out.println("GranFar");
    }
}


class FarA extends GranFar {
    @Override
    void say() {
        System.out.println("FarA");
    }
}

class FarB extends GranFar {
    @Override
    void say() {
        System.out.println("FarB");
    }
}

class Son extends FarA, FarB{ // 컴파일 에러
    @Override
    void say() {
        super.say(); // FarA와 FarB 모두 say 메서드가 있기에 어느 클래스의 메서드 호출인지 모름
    }
}

인터페이스의 경우

public interface GranFar {
    void say();
}


interface FarA extends GranFar {
    @Override
    void say();
}

interface FarB extends GranFar {
    @Override
    void say();
}

interface Son extends FarA, FarB { // 컴파일 에러가 발생하지 않음
    @Override
    void say(); // 상위 인터페이스에서 구현된 것이 없기에 에러가 발생하지 않는다.

}

  • 자바 8부터 등장한 default 메서드를 사용한다면 인터페이스에서도 기능을 구현할 수 있다.

  • 하지만 default 메서드를 사용한다면 클래스처럼 다중 상속 문제가 발생하고, 이를 재정의를 통해 해결해주어야 한다.

public interface GranFar {
    default void say() {
        System.out.println("GranFar");
    }
}


interface FarA extends GranFar {
    @Override
    default void say() {
        System.out.println("FarA");
    }
}

interface FarB extends GranFar {
    @Override
    default void say() {
        System.out.println("FarB");
    }
}

interface Son extends FarA, FarB { // 컴파일 에러
}

  • 위 상태에선 say 메서드 실행시 FarA, FarB 둘 중 어느 인터페이스의 default 메서드를 따라야 할지 모르기 때문에 컴파일 에러가 발생한다.

  • 때문에 어떤 인터페이스의 메서드를 호출해야하는지 명시해줘야 한다.

interface Son extends FarA, FarB { // 명시해줬기에 컴파일 에러 발생하지 않음

    @Override
    default void say() {
        FarA.super.say();
    }
}

동일한 시그니처: 메서드의 접근 제어자, 리턴 타입, 메서드 명, 매개변수가 모두 동일함(구현부는 다를 수 있음)

Last updated