인터페이스
클래스가 반드시 구현해야 할 메서드의 규격(계약)을 정의한 틀입니다.
핵심 개념
// 인터페이스 = "이 기능을 반드시 구현하라"는 약속
interface Flyable {
void fly(); // 구현 없이 선언만
}
// 계약을 이행 (implements)
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("날개로 날다"); // 반드시 구현
}
}
class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("엔진으로 날다"); // 각자 다르게 구현
}
}
왜 쓰는가?
// 인터페이스 덕분에 구체 타입을 몰라도 동일하게 사용 가능
Flyable[] flyables = { new Bird(), new Airplane() };
for (Flyable f : flyables) {
f.fly(); // 실제 구현이 뭔지 몰라도 됨 → 다형성
}
특징
| 항목 | 내용 |
|---|---|
| 메서드 | 선언만 (Java 8+ default 메서드 예외) |
| 필드 | 상수(public static final)만 가능 |
| 생성자 | X 불가 |
| 다중 구현 | O 여러 인터페이스 동시 구현 가능 |
// 다중 구현
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("수영하다"); }
}
실무에서의 활용 (Spring)
// 인터페이스로 규격 정의
public interface UserRepository {
User findById(Long id);
void save(User user);
}
// 구현체는 언제든 교체 가능
public class MySQLUserRepository implements UserRepository { ... }
public class MockUserRepository implements UserRepository { ... } // 테스트용
// Service는 인터페이스에만 의존 → DIP 원칙
public class UserService {
private final UserRepository repo; // 구현체가 뭔지 몰라도 됨
public UserService(UserRepository repo) { this.repo = repo; }
}
인터페이스는 "무엇을 해야 하는지"만 정의하고, "어떻게 하는지"는 구현체에게 위임하는 것이 핵심입니다. 이를 통해 코드 간 결합도를 낮추고 교체/테스트가 쉬운 구조를 만들 수 있습니다.