Blog
스터디
CS Study with SON
5주차
추상클래스와 인터페이스

추상클래스와 인터페이스

추상 메소드(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)

추상 클래스와 인터페이스

추상 클래스인터페이스
사용 키워드abstractinterface
사용 가능 변수제한 없음static final(상수)
사용 가능 접근 제어자제한 없음public
상속 키워드extendsimplements
다중 상속 여부불가능가능
  • 공통점
    • 인스턴스(객체)는 생성할 수 없음(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();
    }
}

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