이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.
공부 › Java › 클래스와 객체
인터페이스와 추상 클래스
둘 다 new로 못 만드는데 무엇이 다른가
인터페이스와 추상 클래스는 둘 다 "이런 메서드가 있어야 한다"는 약속을 정하고, 실제 내용은 구현하는 클래스에 맡깁니다. 하지만 여러 개를 동시에 물려받을 수 있는지, 상태(필드)를 가질 수 있는지가 다릅니다.
실행으로 대조 · JDK 21에서 열두 가지 조합을 컴파일·실행해 대조
이 실습에 나오는 개념
- 인터페이스
- 클래스가 구현해야 할 메서드의 목록(계약)을 정의한 타입이다. 인스턴스 필드를 가질 수 없고, 한 클래스가 여러 인터페이스를 구현할 수 있다. 사용하는 코드가 인터페이스에만 의존하면 구현 클래스를 바꾸거나 테스트용 가짜 구현으로 교체하기 쉽다.
- 추상 클래스
- abstract로 선언되어 직접 인스턴스를 만들 수 없는 클래스다. 구현이 없는 추상 메서드와 구현이 있는 일반 메서드를 함께 가질 수 있고, 필드(상태)와 생성자도 가질 수 있다. 여러 하위 클래스가 공유하는 상태와 공통 로직을 모아 두는 데 쓰며, 클래스는 하나만 상속할 수 있다.
- default 메서드
- Java 8부터 인터페이스에 구현 내용을 가진 메서드를 둘 수 있게 한 기능이다. 인터페이스에 새 메서드를 추가해도 기존 구현 클래스들이 깨지지 않게 하기 위해 도입되었다. 여러 인터페이스의 default 메서드가 충돌하면 구현 클래스에서 직접 재정의해야 한다.
- 추상화
- 대상의 복잡한 세부 사항을 감추고 필요한 핵심 특성만 드러내는 것이다. 자바에서는 인터페이스나 추상 클래스로 "무엇을 할 수 있는지"만 선언하고 구체적인 방법은 구현 클래스에 맡긴다. 사용하는 쪽은 구현을 몰라도 선언된 기능만 보고 쓸 수 있다.
- 상속
- 기존 클래스(상위 클래스)의 필드와 메서드를 물려받아 새 클래스(하위 클래스)를 정의하는 것이다. 공통 코드를 재사용하고 하위 클래스에서 필요한 부분만 추가하거나 재정의한다. 자바의 클래스는 단일 상속만 가능하며, 상위 클래스 변경이 하위 클래스 전체에 영향을 주므로 "is-a" 관계가 분명할 때만 쓰는 것이 좋다.
- 다형성
- 같은 타입의 참조로 같은 메서드를 호출해도 실제 객체의 종류에 따라 다른 동작이 실행되는 성질이다. 상위 타입(인터페이스나 상위 클래스) 변수에 여러 하위 타입 객체를 담을 수 있고, 어떤 구현이 실행될지는 실행 시점에 결정된다(동적 바인딩). 호출하는 코드를 바꾸지 않고 새로운 구현을 추가할 수 있게 해 준다.
이렇게 바꿔 보세요
- 처음 설정으로 — 인터페이스는 여러 개를 동시에 구현할 수 있습니다.
- 추상 클래스 두 개를 상속하면 — 클래스는 하나만 상속할 수 있어 컴파일되지 않습니다.
- 인터페이스에 필드와 생성자 — 인터페이스는 객체의 상태를 가질 수 없습니다.
- 추상 클래스에 필드와 생성자 — 공통 상태와 초기화 코드를 물려줄 수 있습니다.
- 직접 new 하기 — 둘 다 그 자체로는 객체를 만들 수 없습니다.
- 추상 메서드를 구현하지 않으면 — 약속한 메서드가 빠지면 컴파일러가 막습니다.
처음 설정으로 실행하면
- Payable은 인터페이스입니다. "pay()라는 메서드가 있어야 한다"는 약속만 정해 두고, 어떻게 결제할지는 구현하는 클래스가 정합니다.
- Card가 Payable과 Refundable 두 인터페이스를 동시에 구현합니다. 인터페이스는 상태가 없어서 여러 개를 겹쳐도 필드 충돌은 없습니다(같은 default 메서드가 겹치면 직접 정해 줘야 합니다).
- 컴파일됩니다. Card 객체는 Payable 타입 변수에도, Refundable 타입 변수에도 담을 수 있습니다.
실행 결과
- 클래스는 하나만 상속(extends)할 수 있고, 인터페이스는 여러 개를 구현(implements)할 수 있습니다. "~을 할 수 있다"는 능력을 여러 개 붙이려면 인터페이스를 씁니다.
- 고르는 기준: 서로 다른 종류의 클래스들에게 "이 기능을 제공한다"는 약속을 붙이려면 인터페이스, 가까운 종류의 클래스들이 필드와 코드를 공유해야 하면 추상 클래스입니다. 실무에서는 인터페이스를 기본으로 쓰는 경우가 많습니다.
코드
interface Payable {
void pay(); // 추상 메서드: 본문 없음
default String name() { return "기본 이름"; }
}
interface Refundable { void refund(); }
class Card implements Payable, Refundable {
public void pay() { }
public void refund() { }
}
이 모형의 전제
열두 가지 조합 모두, 화면의 코드와 같은 구조의 소스를 이 PC의 JDK 21에서 실제로 컴파일하고 실행한 결과와 대조했습니다(성공 여부와 오류 메시지). 인터페이스의 private 메서드와 static 메서드, sealed 타입 같은 이후 버전의 기능은 다루지 않았습니다.
앞 실습: 다형성과 메서드 선택 · 다음 실습: 접근 제어자와 캡슐화