인터페이스

클래스가 반드시 구현해야 할 메서드의 규격(계약)을 정의한 틀입니다.

핵심 개념

// 인터페이스 = "이 기능을 반드시 구현하라"는 약속
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; }
}

인터페이스는 "무엇을 해야 하는지"만 정의하고, "어떻게 하는지"는 구현체에게 위임하는 것이 핵심입니다. 이를 통해 코드 간 결합도를 낮추고 교체/테스트가 쉬운 구조를 만들 수 있습니다.

CSLOG에서 보기