자바 인터페이스

Dear·2025년 5월 20일

TIL

목록 보기
25/74

💙 인터페이스

객체의 사용 방법을 정의한 타입이다.

개발 코드는 인터페이스에 정의된 메소드만 알고 있어도 된다.
실제 동작은 인터페이스를 구현한 객체가 수행하므로, 개발 코드는 객체의 내부 구조를 몰라도 인터페이스를 통해 메소드를 호출 할 수 있다.

객체는 인터페이스에서 정의된 요소들을 가지고 있어야 한다.
그러면 개발 코드를 수정하지 않고, 사용하는 객체를 변경할 수 있다.

// 1. 인터페이스 정의
interface Animal {
    void sound();  // 메소드 선언만 있음 (구현 X)
}

// 2. 인터페이스를 구현한 클래스
class Dog implements Animal {
    public void sound() {
        System.out.println("멍멍!");
    }
}

class Cat implements Animal {
    public void sound() {
        System.out.println("야옹~");
    }
}

// 3. 개발 코드 (main)
public class Main {
    public static void main(String[] args) {
        Animal a1 = new Dog();  // 인터페이스 타입으로 Dog 객체 사용
        Animal a2 = new Cat();  // 인터페이스 타입으로 Cat 객체 사용

        a1.sound();  // "멍멍!" 출력
        a2.sound();  // "야옹~" 출력
    }
}

인터페이스는 기능 명세만 제공하고 실제 구현은 클래스가 한다.

그럼 왜 인터페이스를 사용하는가??

유연성과 확장성, 즉 다형성을 위해서이다.

1. 다형성

Animal이라는 하나의 타입으로 Dog, Cat, Bird 등 다양한 객체를 같은 방식으로 사용할 수 있기 때문이다.

Animal a1 = new Dog();
Animal a2 = new Cat();
Animal a3 = new Bird();  // 새로 추가해도 문제 없음
// 나중에 클래스가 추가되어도 기존 코드는 바꿀 필요가 없다.

2. 코드 의존성 줄이기 (구현의존X)

// 나쁜 예: 구체 클래스에 직접 의존
Dog d = new Dog();
d.sound();
// Dog가 바뀌거나 Cat으로 바꾸려면 코드를 바꿔야 함.

// 좋은 예: 인터페이스에 의존
Animal a = new Dog();  // 나중에 Cat()으로 바꿔도 문제 없음
a.sound();
// Animal이라는 공통된 타입으로 프로그램을 구성하면,
코드를 수정하지 않고 다양한 객체 사용 가능

3. 유지보수성과 테스트 용이성

// 테스트할 때 Dog 객체 대신 MockAnimal 같은 가짜 객체를 넣을 수 있다
void makeSound(Animal animal) {
    animal.sound();
}

인터페이스만으로 객체 조작

// 인터페이스 정의
interface PaymentService {
    void pay(int amount);
}

// 구현 클래스 A – 카드 결제
class CardPayment implements PaymentService {
    public void pay(int amount) {
        System.out.println(amount + "원을 카드로 결제했습니다.");
    }
}

// 구현 클래스 B – 간편결제
class EasyPayment implements PaymentService {
    public void pay(int amount) {
        System.out.println(amount + "원을 간편결제로 결제했습니다.");
    }
}

// 인터페이스만 사용
public class Order {
    private PaymentService paymentService;

    // 생성자를 통해 구현 객체 주입
    public Order(PaymentService paymentService) {
        this.paymentService = paymentService;
    }

    public void checkout(int amount) {
        paymentService.pay(amount);  // 인터페이스만 보고 호출
    }
}

// 사용 예
public class Main {
    public static void main(String[] args) {
        // 카드 결제 사용
        PaymentService card = new CardPayment();
        Order order1 = new Order(card);
        order1.checkout(10000);  // → "10000원을 카드로 결제했습니다."

        // 간편결제로 변경 (Order 클래스는 수정 없음!)
        PaymentService easy = new EasyPayment();
        Order order2 = new Order(easy);
        order2.checkout(20000);  // → "20000원을 간편결제로 결제했습니다."
    }
}

💙 선언

상수 필드

데이터를 저장할 수 있는 필드는 선언할 수 없다.

상수필드만 선언 가능하다.(초기값 대입 필수) public static final을 생략하더라도 자동적으로 붙는다.

추상 메소드

어떤 매개값이 필요하고 리턴 타입이 무엇인지만 선언한다.

실제 실행부는 객체(구현 객체)가 가지고 있다.

인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행되기 때문에 인터페이스의 모든 메소드는 실행 블록이 필요 없는 추상 메소드로 선언한다.

인터페이스에 선언된 추상 메소드는 public abstract를 생략하더라도 자동적으로 컴파일 과정에서 붙게 된다.

디폴트 메소드

인터페이스에 선언되지만 사실은 객체(구현 객체)가 가지고 있는 인스턴스 메소드.
public 생략 가능

정적 메소드

디폴트 메소드와 달리 객체가 없어도 인터페이스만으로 호출 가능하다.
public 생략 가능

💙 익명 구현 객체

익명 구현 객체는 소스 파일을 만들지 않고도 구현 객체를 만들 수 있는 방법이다.
람다식 사용
하나의 실행문이므로 끝에는 반드시 세미콜론(;)을 붙여야 한다.

public class RemoteControllerExample{
	public static void main(String[] args){
		RemoteControl rc = new RemoteControl(){ // RemoteControl -> interface / new 연산자로 클래스 캑체 생성
			public void turnOn() { /*실행문*/ }
			public void turnOff() { /*실행문*/ }
			public void setVolume(int volume) { /*실행문*/}
		};
	}
}

🤍 회고

오늘은 인터페이스에 집중해 공부했다. 사용법은 이해했지만, 실제 상황에서 어떻게 활용되는지는 아직 감이 잘 안 온다. 프로젝트에 직접 적용해보면서 인터페이스의 활용법을 몸으로 익히고 싶다.

profile
친애하는 개발자

0개의 댓글