자바에서 추상화를 구현하기 위한 가장 기본적이고 강력한 도구
인터페이스는 객체들이 서로 상호작용을 할 수 있도록 돕는 약속된 규격을 정의하는 장치이다
- interface는 클래스가 반드시 구현해야 할 기능의 목록(추상 메서드)만 정의한다
- 기본적으로 모든 메서드는 public abstract, 모든 필드는 public static final로 간주된다
- 구현체 클래스는 인터페이스에 정의된 모든 메서드를 반드시 구현해야만 한다
- 인터페이스는 다중 구현(multiple inheritance)이 가능하다
기본 구조
public interface InterfaceEx {
int ROCK = 1; // public static final 생략 가능
int SCISSORS = 2;
int PAPER = 3;
String getPlayingNum() { // public abstract 생략 가능
void call();
}
}
* 모든 필드는 자동으로 public static final로 처리되며, 생략할 수 있다
* 모든 메서드는 자동으로 public abstract로 처리되며, 생략할 수 있다
* 메서드 바디는 가질 수 없는데, Java 8부터는 default, static 메서드가 허용된다
인터페이스의 다중 구현
일반적으로 자바는 클래스간 다중 상속을 허용하지 않지만, 인터페이스는 여러개를 동시에 구현할 수 있다
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
class Duck implements Flyable, Swimmable { // implements는 여러개를 구현할 수 있다
public void fly() {
System.out.println("날아간다");
}
public void swimmable() {
System.out.println("수영한다");
}
}
추상 클래스 vs 인터페이스
| 항목 | 추상 클래스 | 인터페이스 |
| 인스턴스 생성 | 불가능 | 불가능 |
| 상속 / 구현 키워드 | extends | implements |
| 다중 구현 | 불가능 | 가능 |
| 멤버 구성 | 필드, 생성자, 메서드 등 모두 가능 | 추상 메서드, 상수, default/static 메서드(Java 8 이상) |
| 사용 목적 | 공통 로직 공유 + 강제 구현 | 강제 구현 중심, 역할 명세 |
인터페이스의 장점
1. 역할과 구현의 분리로 인한 유연성
- 인터페이스를 사용하면 사용자(User)는 실제 구현 클래스(Provider)의 변경이나 교체와 무관하게 기능을 사용할 수 있다
- 유지보수성과 확장성 측면에서 큰 장점이며, 시스템이 커질수록 효과가 더 커진다
Provider 클래스에 의존하는 구조
public class InterfaceExample {
public static void main(String[] args) {
User user = new User();
user.callProvider(new Provider());
}
}
class User {
public void callProvider(Provider provider) {
provider.call();
}
}
class Provider {
public void call() {
System.out.println("야호");
}
}
class Provider2 {
public void call() {
System.out.println("이얏호오");
}
}
위 구조에서 Provider가 Provider2로 교체된다면, User 클래스 또한 함께 수정되어야한다
class User {
public void callProvider(Provider2 provider) { // Provider2로 변경됨
provider.call();
}
}
사용 클래스 변경이 필요해지므로 유지보수가 까다로워지는 결과를 낳는다
인터페이스로 개선한 구조

interface Cover {
void call();
}
class Provider implements Cover {
public void call() {
System.out.println("야호");
}
}
class Provider2 implements Cover {
public void call() {
System.out.println("이얏호오");
}
}
class User {
public void callProvider(Cover cover) {
// User 클래스 메서드의 매개변수에 인터페이스 Cover를 넣음
cover.call();
}
}
public class InterfaceTest {
public static void main(String[] args) {
User user = new User(); // User 객체 생성
// Provider2는 Cover를 implements 하고 있으므로 이러한 객체 생성 후 주입이 가능하다
user.callProvider(new Provider2());
}
}
* User 클래스는 더이상 구현 클래스에 직접적으로 의존하지 않으며, Cover 인터페이스를 통해 동작한다
* Provider2를 전달하더라도 User 클래스의 코드는 변경이 필요하지 않게 된다
실전에서 : 역할과 구현의 분리
interface MessageSender {
void send();
}
class EmailSender implements MessageSender {
public void send() {
System.out.println("이메일 전송");
}
}
class SmsSender implements MessageSender {
public void send() {
System.out.println("SMS 전송");
}
}
// 얘는 구현 클래스가 아니기 때문에 사용하기 까다롭다
class KakaoSender {
public void send() {
System.out.println("SMS 전송");
}
}
class NotificationService {
public void notify(MessageSender sender) {
sender.send();
}
}
class App {
public static void main(String[] args) {
NotificationService service = new NotificationService();
service.notify(new EmailSender());
service.notify(new SmsSender());
// service.notify(new KakaoSender()); 는 불가능하다.
//KakaoSender는 MessageSender의 구현체가 아니기 때문
}
}
* 인터페이스 MessageSender를 통해 다양한 전송수단을 유연하게 교체할 수 있다
요약
- 인터페이스는 기능 명세의 집합으로, 다형성과 느슨한 결합을 위한 핵심 수단이다
- 모든 클래스는 여러 인터페이스를 구현할 수 있기 때문에 자바의 단일 상속의 한계를 극복할 수 있다
- 인터페이스를 잘 활용하면, 유지보수성과 재사용성인 높은 코드 구조를 설계할 수 있다
'JAVA > 추상화(Abstraction)' 카테고리의 다른 글
| 추상 클래스와 인터페이스, 선택 기준과 활용 전략에 대해 (0) | 2026.04.21 |
|---|---|
| final 키워드 (0) | 2026.04.21 |
| 추상 클래스 (0) | 2026.04.20 |
| abstract 제어자 (0) | 2026.04.20 |
| 추상화(Abstraction) (0) | 2026.04.20 |