본문 바로가기

JAVA

상속 extends 와 규약 interface 의 차이


 1️⃣ 인터페이스는 “상속”이라기보단 “규약(약속)”

자바에서 interface는 일종의 ‘설명서’ 혹은 ‘계약서’입니다.

즉,

“이 인터페이스를 구현하는 클래스는
여기에 적힌 메서드를 반드시 만들어야 한다.”

라는 규칙(Contract) 을 강제하는 역할이에요.


📘 예시

// 인터페이스 (약속서)
public interface Animal {
    void sound();  // 몸체가 없는 메서드 (규칙만 정의)
}
// 구현 클래스
public class Dog implements Animal {
    @Override
    public void sound() {
        System.out.println("멍멍!");
    }
}

이 구조는 ‘상속’보다는 ‘구현(implementation)’이에요.

👉 extends는 상속,
👉 implements는 인터페이스 구현.


🧩 2️⃣ “상속”과 “인터페이스 구현”의 차이

구분 상속 (extends) 인터페이스 (implements)

관계 부모 → 자식 (기능을 물려받음) 규칙을 따름 (형태만 구현)
키워드 extends implements
메서드 부모의 메서드를 그대로 상속 가능 반드시 인터페이스의 메서드를 구현해야 함
다중 가능 여부 ❌ 단일 상속만 가능 ✅ 여러 인터페이스 구현 가능
목적 코드 재사용 공통 규약 강제

📘 예시로 비교

클래스 상속

class Parent {
    void hello() { System.out.println("Hello from Parent"); }
}

class Child extends Parent {
    void bye() { System.out.println("Bye from Child"); }
}

➡️ Child는 Parent의 기능을 “물려받음”


인터페이스 구현

interface Flyable {
    void fly();  // 구현해야 함
}

class Bird implements Flyable {
    public void fly() { System.out.println("새가 납니다"); }
}

➡️ Bird는 Flyable의 규칙을 “이행함”


⚙️ 3️⃣ 인터페이스는 다중 “구현” 가능

자바는 클래스는 하나만 상속 가능하지만,
인터페이스는 여러 개 구현 가능해요 👇

interface Flyable { void fly(); }
interface Swimmable { void swim(); }

class Duck implements Flyable, Swimmable {
    public void fly() { System.out.println("날아오른다!"); }
    public void swim() { System.out.println("헤엄친다!"); }
}

➡️ 이런 다중 행동 정의가 인터페이스의 큰 장점


4️⃣ 정리하자면

항목 클래스 상속 인터페이스

키워드 extends implements
관계 부모 → 자식 규약 → 구현체
목적 코드 재사용 기능 약속
메서드 구현 선택적(Override 가능) 필수(Override 해야 함)
다중 적용 불가능 가능
실제 표현 “is-a 관계” “can-do 관계”

✅ 한 줄 요약

🔸 인터페이스는 “상속”이 아니라 “규약(약속)”을 구현(implements) 하는 구조

🔸 클래스 상속(extends)은 부모의 기능을 물려받는 것,
인터페이스 구현(implements)은 정해진 형식을 따라가는 것.

 

인터페이스의 메서드는 “반드시 오버라이드해야 하지만”, @Override 키워드는 선택사항


1️⃣ 인터페이스의 메서드는 “무조건 구현”해야 함

인터페이스 안의 메서드는 기본적으로 전부 abstract(추상 메서드)
즉, 구현이 없고, 형태만 있다

public interface Animal {
    void sound();  // 구현(몸체) 없음
}

이걸 구현(implements)하는 클래스는 반드시 sound()를 만들어야 한다.

public class Dog implements Animal {
    public void sound() {       // ✅ 오버라이드 필요
        System.out.println("멍멍!");
    }
}

만약 구현 안 하면 이렇게 에러  👇

❌ Class 'Dog' must either be declared abstract or implement abstract method 'sound()' in 'Animal'


2️⃣ @Override는 “붙이는 게 원칙이지만, 필수는 아님”

@Override는 단지 “이 메서드는 부모(또는 인터페이스)의 메서드를 재정의한 것”
라고 컴파일러에 알려주는 표시용 어노테이션

public class Dog implements Animal {
    @Override       // 권장 👍
    public void sound() {
        System.out.println("멍멍!");
    }
}

위처럼 써야:

  • 메서드 이름/시그니처가 잘못되면 컴파일 에러로 알려줌
  • 실수(예: 오타) 방지

안 써도 실행은 됩니다 👇

public class Dog implements Animal {
    public void sound() {  // @Override 없어도 동작은 함
        System.out.println("멍멍!");
    }
}

 

 

 3️⃣ 실무에서는 “항상 붙인다”가 표준

이유 설명

컴파일러 체크 기능 부모 인터페이스에 진짜 메서드가 있는지 확인
코드 가독성 향상 협업 시, 재정의된 메서드라는 걸 한눈에 파악
유지보수 용이 리팩토링 시 안전하게 추적 가능

🔸 그래서 실무에서는 “인터페이스든, 상속이든, 오버라이드할 땐 무조건 @Override 붙이는 게 기본 습관”이에요.


💡 요약 정리

구분 의미 필수 여부

인터페이스 메서드 구현 반드시 구현해야 함 ✅ 필수
@Override 어노테이션 “이 메서드는 부모의 메서드 재정의임” ⚙️ 선택 (하지만 권장)

한 줄 정리

인터페이스의 메서드는 무조건 “오버라이드해서 구현”해야 하고,
@Override는 “붙이는 게 원칙이지만 없어도 돌아는 간다.”


원하면 “인터페이스 오버라이드 vs 상속 오버라이드”를
실행 순서까지 보여주는 간단한 예제로 정리해줄까?
(예: 부모 → 인터페이스 → 자식 클래스 순으로 호출 흐름)