자바 인터페이스 이해하기: 정의, 용도, 모범 사례
인터페이스는 자바에서 참조 타입입니다. 클래스와 유사합니다. 추상 메서드들의 모음입니다. 클래스가 인터페이스를 구현하면 인터페이스의 추상 메서드들을 상속받습니다.
추상 메서드 외에도 인터페이스에는 상수, 디폴트 메서드, 정적 메서드, 중첩 타입이 포함될 수 있습니다. 메서드 본문은 디폴트 메서드와 정적 메서드에만 존재합니다.
인터페이스 작성은 클래스 작성과 유사합니다. 하지만 클래스는 객체의 속성과 행동을 설명합니다. 인터페이스는 클래스가 구현하는 행동들을 포함합니다.
인터페이스를 구현하는 클래스가 추상이 아닌 한, 인터페이스의 모든 메서드는 클래스에서 정의되어야 합니다.
인터페이스는 다음 면에서 클래스와 유사합니다.
- 인터페이스는 임의의 개수 메서드를 포함할 수 있음
- 인터페이스는 인터페이스 이름과 같은 이름의 .java 확장자 파일에 작성됨
- 인터페이스의 바이트코드는 .class 파일에 나타남
- 인터페이스들은 패키지에 속하며, 해당 바이트코드 파일은 패키지 이름과 일치하는 디렉터리 구조에 있어야 함
그러나 인터페이스는 여러 면에서 클래스와 다릅니다.
- 인터페이스를 인스턴스화할 수 없음
- 인터페이스는 생성자를 포함하지 않음
- 인터페이스의 모든 메서드는 추상적임
- 인터페이스는 인스턴스 필드를 가질 수 없음. 인터페이스에 나타날 수 있는 필드는 static과 final로 모두 선언되어야 함
- 인터페이스는 클래스에 의해 확장되지 않음; 클래스에 의해 구현됨
- 인터페이스는 여러 인터페이스를 확장할 수 있음
인터페이스 선언
interface 키워드로 인터페이스를 선언합니다. 간단한 예제입니다.
예제
/* File name: NameOfInterface.java */
import java.lang.*; // 임의의 import 문들
public interface NameOfInterface {
// 임의의 개수 final, static 필드들
// 임의의 개수 추상 메서드 선언들
}
인터페이스는 다음 속성들을 가집니다.
- 인터페이스는 암묵적으로 abstract입니다. 인터페이스 선언 시 abstract 키워드를 사용할 필요 없음
- 인터페이스의 각 메서드도 암묵적으로 abstract이므로 abstract 키워드 불필요
- 인터페이스의 메서드들은 암묵적으로 public임
예제
/* File name: Animal.java */
interface Animal {
public void eat();
public void travel();
}
인터페이스 구현
클래스가 인터페이스를 구현하면, 그 클래스는 인터페이스의 특정 행동들을 수행하기로 계약하는 것으로 생각할 수 있습니다. 클래스가 인터페이스의 모든 행동을 수행하지 않으면 그 클래스는 abstract로 선언해야 합니다.
클래스는 implements 키워드로 인터페이스를 구현합니다. implements 키워드는 클래스 선언에서 extends 부분 다음에 옵니다.
예제
/* File name: MammalInt.java */
public class MammalInt implements Animal {
public void eat() {
System.out.println("Mammal eats");
}
public void travel() {
System.out.println("Mammal travels");
}
public int noOfLegs() { return 0; }
public static void main(String args[]) {
MammalInt m = new MammalInt();
m.eat();
m.travel();
}
}
실행 결과
Mammal eats
Mammal travels
인터페이스에 정의된 메서드들을 오버라이드할 때 따라야 할 규칙들:
- 체크 예외는 인터페이스 메서드에서 선언된 것들 또는 그 하위 클래스들 외에는 구현 메서드에서 선언할 수 없음
- 인터페이스 메서드의 시그니처와 같은 반환 타입 또는 하위 타입을 유지해야 함
- 구현 클래스 자체가 abstract일 수 있으며, 그렇다면 인터페이스 메서드들을 구현할 필요 없음
인터페이스 구현 시 규칙들:
- 클래스는 한 번에 둘 이상의 인터페이스를 구현할 수 있음
- 클래스는 하나의 클래스만 확장할 수 있지만 여러 인터페이스를 구현할 수 있음
- 인터페이스는 다른 인터페이스를 확장할 수 있으며, 이는 클래스가 다른 클래스를 확장하는 것과 유사함
인터페이스 확장
인터페이스는 클래스가 다른 클래스를 확장하는 것과 같은 방식으로 다른 인터페이스를 확장할 수 있습니다. extends 키워드로 인터페이스를 확장하며, 자식 인터페이스는 부모 인터페이스의 메서드들을 상속받습니다.
다음 Sports 인터페이스는 Hockey와 Football 인터페이스에 의해 확장됩니다.
예제
// Filename: Sports.java
public interface Sports {
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}
// Filename: Football.java
public interface Football extends Sports {
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}
// Filename: Hockey.java
public interface Hockey extends Sports {
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}
Hockey 인터페이스는 네 개의 메서드를 가지지만 Sports에서 두 개를 상속받으므로, Hockey를 구현하는 클래스는 여섯 개의 메서드를 모두 구현해야 합니다. 마찬가지로 Football을 구현하는 클래스는 Football의 세 메서드와 Sports의 두 메서드를 정의해야 합니다.
다중 인터페이스 확장
자바 클래스는 오직 하나의 부모 클래스만 확장할 수 있습니다. 다중 상속은 허용되지 않습니다. 하지만 인터페이스는 클래스가 아니므로, 인터페이스는 둘 이상의 부모 인터페이스를 확장할 수 있습니다.
extends 키워드는 한 번만 사용되며, 부모 인터페이스들은 콤마로 구분된 리스트로 선언됩니다.
예를 들어 Hockey 인터페이스가 Sports와 Event를 모두 확장한다면 다음과 같이 선언됩니다.
예제
public interface Hockey extends Sports, Event
태깅 인터페이스(Tagging Interfaces)
인터페이스 확장의 가장 일반적인 용도는 부모 인터페이스가 어떤 메서드도 포함하지 않을 때입니다. 예를 들어 java.awt.event 패키지의 MouseListener 인터페이스는 java.util.EventListener를 확장하는데, 이는 다음과 같이 정의됩니다.
예제
package java.util;
public interface EventListener { }
메서드가 없는 인터페이스를 태깅(tagging) 인터페이스라고 합니다. 태깅 인터페이스의 두 가지 기본 설계 목적:
공통 부모 생성 - EventListener 인터페이스처럼 자바 API의 수십 개 다른 인터페이스들에 의해 확장되는 경우, 태깅 인터페이스를 사용하여 인터페이스 그룹의 공통 부모를 만들 수 있습니다. 예를 들어 인터페이스가 EventListener를 확장하면, JVM은 이 인터페이스가 이벤트 위임 시나리오에서 사용될 것임을 알게 됩니다.
클래스에 데이터 타입 추가 - 여기서 '태깅'이라는 용어가 나옵니다. 태깅 인터페이스를 구현하는 클래스는 아무 메서드도 정의할 필요가 없습니다(인터페이스에 메서드가 없으므로). 하지만 그 클래스는 다형성을 통해 인터페이스 타입이 됩니다.