인터페이스란?
클래스가 어떤 기능을 반드시 구현해야 하는지 정해놓은 설계도입니다.
- 메서드의 형태(이름, 매개변수, 리턴 타입)만 정의하고, 구현은 하지 않음
- 클래스가 인터페이스를 implements(구현) 하면, 그 안의 메서드를 무조건 오버라이딩 해야 합니다.
쉽게 말하면,
"이런 기능이 필요해! 하지만 어떻게 만들지는 네가 알아서 해"
👉 이런식으로 필요한 클래스에게 강제로 만들게 하는거죠!
문법
* 인터페이스 (설계도)
interface 인터페이스명 {
반환타입 메서드이름(매개변수);
}
- interface 키워드로 선언
- 메서드의 "이름만" 정의하고, 실제 내용(구현)은 없음
- 모든 메서드는 기본적으로 public abstract (생략해도 자동으로 붙음)
- 클래스에게 "이 메서드는 꼭 만들어!" 라고 기능을 강제하는 틀
* 클래스 (인터페이스를 구현하는 클래스)
class 클래스명 implements 인터페이스명 {
@Override
메서드이름(매개변수) {
// 반드시 기능 구현
}
}
- implements 키워드를 사용해서 인터페이스를 구현
- 인터페이스에서 약속한 모든 메서드를 반드시 오버라이딩해야 함
- @Override는 "이건 약속된 메서드를 구현한 거야!"라는 표시
클래스와 인터페이스 관계 문법 정리
✅ 인터페이스를 상속받은 인터페이스를 구현하는 클래스는,
두 인터페이스 안에 있는 모든 추상 메서드를 구현해야 합니다!
예시 : J 인터페이스를 구현하는 클래스는 J와 I안에 있는 기능을 전부 구현해야함
적용 예시
* Animal 인터페이스
interface Animal {
void sound(); // 몸체 없이 선언만! (추상 메서드)
}
* Dog 클래스 (Animal 인터페이스를 구현하는 클래스)
class Dog implements Animal {
@Override
public void sound() {
System.out.println("멍멍");
}
}
* Cat 클래스 (Animal 인터페이스를 구현하는 클래스)
class Cat implements Animal {
@Override
public void sound() {
System.out.println("야옹");
}
}
* 실행 클래스
public class Main {
public static void main(String[] args) {
Animal a1 = new Dog();
Animal a2 = new Cat();
a1.sound();
a2.sound();
}
}
- 출력 결과 -
멍멍
야옹
인터페이스를 사용하는 이유
인터페이스는 단순한 문법이 아니라,
일관된 설계, 유연한 확장, 실무적인 유지보수를 가능하게 하는 핵심 도구입니다.
1. 일관된 구조로 개발 가능
인터페이스는 클래스들에게
“이 기능은 꼭 만들어야 해!” 라는 기능 구현의 약속을 강제합니다.
예시
* Animal 인터페이스
interface Animal {
void sound(); // 모든 동물이 반드시 구현해야 할 기능
}
* Dog, Cat 클래스 (Animal 인터페이스를 구현하는 클래스)
class Dog implements Animal {
public void sound() {
System.out.println("멍멍");
}
}
class Cat implements Animal {
public void sound() {
System.out.println("야옹");
}
}
- 다양한 클래스가 같은 메서드 이름(sound)을 가지도록 일관성 유지 가능
- 협업 시 명확한 규칙 제공 → “인터페이스만 보면 구현 방법은 몰라도, 뭘 제공해야 하는지는 알 수 있음”
2. 다형성과 함께 사용하면 유연한 코드 가능
인터페이스 타입으로 다양한 객체를 받을 수 있어,
코드 수정 없이 기능 확장이 매우 쉬워집니다.
예시
public class Main2 {
public static void main(String[] args) {
makeSound(new Dog()); // 멍멍
makeSound(new Cat()); // 야옹
//makeSound(new Tiger()); // 필요에 따라 추가해도 코드 수정 없음
}
public static void makeSound(Animal animal) {
animal.sound(); // 어떤 동물이든 sound()는 존재하니까 호출 가능!
}
}
- makeSound() 메서드는 오직 Animal 인터페이스 타입만 알면 됨
- 내부에 어떤 객체가 들어와도 sound()는 동작함 → 다형성
- 새로운 동물을 추가해도 기존 코드는 수정할 필요 없음 → 확장성 Good
3. 여러 인터페이스를 동시에 구현 가능 (다중 구현)
자바에서는 클래스는 하나만 상속 가능하지만,
인터페이스는 여러 개를 동시에 구현 가능합니다.
👉 여러개의 인터페이스를 다중으로 구현 할 경우
implements 인터페이스1, 인터페이스2, ...
형식처럼 콤마로 나타내서 사용하면 됩니다!
예시
* Flyable 인터페이스
interface Flyable {
void fly();
}
* Swimmable 인터페이스
interface Swimmable {
void swim();
}
* Duck 클래스 ( Flyable, Swimmable 인터페이스 다중 상속)
class Duck implements Flyable, Swimmable {
public void fly() {
System.out.println("오리가 납니다");
}
public void swim() {
System.out.println("오리가 헤엄칩니다");
}
}
- 클래스 하나가 여러 역할을 동시에 수행 가능
- 기능을 작게 나눠서 조합하듯 사용하는 게 인터페이스의 큰 장점
자바는 클래스 다중 상속은 불가능하지만,
인터페이스는 여러 개를 동시에 구현 가능합니다.
클래스가 implements A, B, C처럼 여러 역할을 동시에 가질 수 있습니다.
이를 통해 복잡한 기능을 작게 쪼개서 조합형 설계가 가능해집니다.
👉 이게 실무에서 인터페이스 설계가 중요한 이유예요!
사용 예시
- Payment 인터페이스는 결제 기능을 나타냅니다.
- 여러 클래스가 이를 구현해서 각기 다른 결제 방식을 표현합니다.
- 그리고 PointUsable이라는 보조 인터페이스를 추가해서,
"포인트도 함께 사용 가능한 결제 수단"을 표현합니다.
* Payment 인터페이스
interface Payment {
void pay(int amount);
}
* PointUsable 인터페이스
interface PointUsable {
void usePoint(int point); // 포인트 사용 기능 (선택적)
}
* NhnKcp 클래스 (Payment, PointUsable 인터페이스를 구현하는 클래스)
class NhnKcp implements Payment, PointUsable {
@Override
public void pay(int amount) {
System.out.println("NHN KCP로 " + amount + "원 결제");
}
@Override
public void usePoint(int point) {
System.out.println(point + " 포인트를 사용했습니다.");
}
}
* KakaoPay 클래스 (Payment 인터페이스를 구현하는 클래스)
class KakaoPay implements Payment {
public void pay(int amount) {
System.out.println("카카오페이로 " + amount + "원 결제");
}
}
* CreditCard 클래스 (Payment 인터페이스를 구현하는 클래스)
class CreditCard implements Payment {
public void pay(int amount) {
System.out.println("신용카드로 " + amount + "원 결제");
}
}
* 실행 클래스
public class PaymentMain {
public static void main(String[] args) {
processPayment(new NhnKcp(), 10000);
processPayment(new KakaoPay(), 15000);
processPayment(new CreditCard(), 20000);
}
public static void processPayment(Payment payment, int amount) {
payment.pay(amount);
// 만약 포인트도 사용할 수 있는 결제 수단이라면
if (payment instanceof PointUsable) {
((PointUsable) payment).usePoint(1000); // 다운캐스팅
}
System.out.println("-----");
}
}
- 출력 결과 -
NHN KCP로 10000원 결제
1000 포인트를 사용했습니다.
-----
카카오페이로 15000원 결제
-----
신용카드로 20000원 결제
-----
- Payment
➡ 공통 결제 기능 인터페이스 - PointUsable
➡ 추가 기능 인터페이스 (포인트 사용) - NhnKcp implements Payment, PointUsable
➡ 두 인터페이스를 동시에 구현한 예 (다중 구현) - instanceof
➡ 실제 객체가 PointUsable인지 확인 후, 안전하게 다운캐스팅 - processPayment()
➡ 인터페이스 하나로 다양한 결제 객체를 유연하게 처리 (다형성 + 인터페이스 활용)
요약 정리
항목 | 설명 |
선언 키워드 | interface |
구현 키워드 | implements |
메서드 | 선언만 존재, 구현은 없음 |
기능 강제 | 인터페이스를 구현하면 모든 메서드를 반드시 구현해야 하므로, 개발자가 약속을 어기지 않게 도와줌 |
다형성 활용 | 인터페이스 하나로 다양한 구현 객체를 처리할 수 있어, 코드가 유연해짐 |
느슨한 결합 | 객체 간 의존도를 줄이고, 쉽게 바꾸거나 테스트 가능함 (예: NhnKcp → CreditCard) |
유지보수 편리 | 새로운 기능 추가 시 기존 코드를 건드릴 필요가 없어, 시스템 확장에 유리 |
다중 구현 가능 | 클래스는 하나만 상속 가능하지만, 인터페이스는 여러 개를 동시에 구현 가능 |
실무 설계 필수 | 스프링(Spring), JPA, 테스트 코드 등 실무 프레임워크 대부분이 인터페이스 중심 설계 사용 |
✅ 인터페이스는 기능 이름만 정해놓는 약속!
➡ 그 약속을 지켜서 구현하는 건 상속 받은 클래스의 역할입니다!
인터페이스를 활용하면 클래스 간 기능 구조를 명확하게 나눌 수 있고,
여러 기능을 조합한 유연한 설계(다중 구현)도 가능해집니다.
실무에서는 이런 구조가 DI(의존성 주입), 전략 패턴, 프레임워크 확장 등에 적극 사용됩니다.
읽어주셔서 감사합니다 😊
'프로그래밍 언어 > Java' 카테고리의 다른 글
[Java] 31. 내부 클래스란? (0) | 2025.04.18 |
---|---|
[Java] 30. 추상 클래스와 인터페이스의 차이 (1) | 2025.04.18 |
[Java] 28. 추상 클래스란? (1) | 2025.04.17 |
[Java] 27. 추상 메서드란? (0) | 2025.04.17 |
[Java] 26. 업캐스팅, 다운캐스팅, instanceof (0) | 2025.04.17 |