인터페이스란?
인터페이스는 클래스들이 동일한 동작을 보장하도록 설계된 추상화의 한 형태입니다. 인터페이스는 구현 클래스에서 반드시 오버라이딩하여 구현해야 하는 추상 메소드 집합을 선언하고, 이러한 강제성을 통해 일관된 동작을 보장합니다. 인터페이스는 보통 추상클래스와 많이 비교하는데 추상 클래스가 그 추상 클래스를 상속 받아서 추상 클래스에서 선언된 변수나 메소드를 실체 클래스에서 사용하고 확장시키는데 의의가 있다면 인터페이스는 인터페이스에서 선언한 추상 메소드를 자식클래스에서 구현을 강제함으로써 같은 동작을 보장하는데 의의가 있습니다. 또한 인터페이스는 추상클래스와 달리 다중 상속이 가능하며 만약 인터페이스의 추상 메소드를 구현하지 않으면 컴파일 오류가 발생합니다.
인터페이스 구성
상수 필드(Constant Field)
인터페이스도 상수 필드 선언이 가능한데 이 상수는 인터페이스에 고정된 값으로 수정될 수 없습니다. 그러므로 상수를 선언할 때는 반드시 초기값을 대입해야 합니다.
추상 메소드(Abstract Method)
추상 메소드는 객체가 가지고 있는 메소드를 설명한 것으로 호출할 때 어떤 매개값이 필요하고, 리턴 타입이 무엇인지만 알려줍니다. 즉 메소드 선언만 하고 실제 실행부는 인터페이스를 상속받는 구현 클래스가 가지고 있습니다. 각 구현 클래스는 인터페이스에 정의된 추상 메소드를 반드시 오버라이드해야 합니다.
디폴트 메소드(Default Method)
디폴트 메소드는 인터페이스에 선언되지만 사실은 객체(구현 객체)가 가지고 있는 인스턴스 메소드 입니다. 즉, 디폴트 메소드는 인터페이스에서 선언되지만 인터페이스에서 바로 사용할 수 없고 구현 객체를 통해서 사용할 수 있습니다.
정적 메소드(Static Method)
정적 메소드는 디폴트 메소드와는 달리 객체(구현 객체)가 없어도 인터페이스만으로 호출이 가능합니다.
인터페이스 사용법
// 인터페이스 구현
public interface RemoteControl {
// 상수
int MAX_VOLUME = 10;
int MIN_VOLUME = 0;
// 추상 메소드
void turnOn();
void turnOff();
void setVolume(int volume);
// 디폴트 메소드
public default void setMute(boolean mute) {
if (mute) {
System.out.println("무음 처리합니다.");
} else {
System.out.println("무음 해제합니다.");
}
}
// 정적 메소드
public static void changeBattery() {
System.out.println("건전지를 교환합니다.");
}
}
RemoteControl 인터페이스에 장비를 켜고 끌수 있는 turnOn, turnOff 추상메소드와 setVolume 추상 메소드를 선언하고 무음 설정을 할수 있는 setMute 디폴트 메소드와 건전지를 교환하는 changeBattery() 정적 메소드를 구현 했습니다.
// RemoteControl 인터페이스를 상속받아 구현한 Television 구현클래스
public class Television implements RemoteControl {
// 필드
private int volume;
// turnOn()
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}
// turnOff()
@Override
public void turnOff() {
System.out.println("TV를 끕니다.");
}
// setVolume()
@Override
public void setVolume(int volume) {
if (volume > RemoteControl.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 TV 볼륨: " + this.volume);
}
}
// RemoteControl 인터페이스를 상속받아 구현한 Audio 구현클래스
public class Audio implements RemoteControl {
// 필드
private int volume;
// turnOn()
@Override
public void turnOn() {
System.out.println("Audio를 켭니다.");
}
// turnOff()
@Override
public void turnOff() {
System.out.println("Audio를 끕니다.");
}
// setVolume()
@Override
public void setVolume(int volume) {
if (volume > RemoteControl.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 Audio 볼륨: " + this.volume);
}
}
RemoteControl 인터페이스를 구현하고 이를 상속받는 Television, Audio 구현 클래스를 생성하여 인터페이스에서 선언한 turnOn(), turnOff(), setVolume() 추상메소드의 실행부를 구현 했습니다.
public class RemoteControlExample {
public static void main(String[] args) {
RemoteControl rc;
rc = new Television();
rc.turnOn();
rc.turnOff();
rc = new Audio();
rc.turnOn();
rc.turnOff();
}
}
인터페이스로 구현 객체를 사용하려면 위와 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 합니다. RemoteControl 인터페이스에 Television 구현 객체를 대입한 후 실행하면 아래와 같은 결과를 얻을 수 있습니다.
여기까지는 인터페이스에서 선언한 추상 메소드를 구현하고 사용해 보았고 지금부터는 디폴트 메소드와 정적 메소드를 사용해 보겠습니다. 인터페이스 구성 문단에서 설명했듯이 디폴트 메소드는 인터페이스에서 실행부를 구현하지만 인터페이스에서 바로 사용할 수 없습니다. 때문에 추상 메소드와 마찬가지로 구현 객체가 필요합니다.
먼저 추상 메소드의 실행부를 구현했던 Audio 클래스에 디폴트 메소드를 재정의 했습니다.
// RemoteControl 인터페이스를 상속받아 구현한 Audio 구현클래스
public class Audio implements RemoteControl {
// 필드
private int volume;
private boolean mute;
// turnOn()
@Override
public void turnOn() {
System.out.println("Audio를 켭니다.");
}
// turnOff()
@Override
public void turnOff() {
System.out.println("Audio를 끕니다.");
}
// setVolume()
@Override
public void setVolume(int volume) {
if (volume > RemoteControl.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 Audio 볼륨: " + this.volume);
}
// setMute()
@Override
public void setMute(boolean mute) {
this.mute = mute;
if (mute) {
System.out.println("Audio 무음 처리합니다.");
} else {
System.out.println("Audio 무음 해제합니다.");
}
}
}
public class RemoteControlExample {
public static void main(String[] args) {
RemoteControl rc;
rc = new Television();
rc.turnOn();
rc.turnOff();
rc = new Audio();
rc.turnOn();
rc.turnOff();
rc.setMute(true);
rc.setMute(false);
}
}
이렇게 setMute() 추상 메소드를 Audio 구현클래스에서 재정의 하고 main 메소드를 실행하면 아래와 같은 결과를 얻을 수 있습니다.
디폴트 메소드와 달리 정적 메소드는 구현부 없이 인터페이스에서 바로 호출이 가능합니다.
public class RemoteControlExample {
public static void main(String[] args) {
RemoteControl rc;
rc = new Television();
rc.turnOn();
rc.turnOff();
rc = new Audio();
rc.turnOn();
rc.turnOff();
rc.setMute(true);
rc.setMute(false);
// 인터페이스에서 바로 changeBattery() 정적 메소드 호출
RemoteControl.changeBattery();
}
}
위와 같이 RemoteControl 인터페이스에서 바로 changeBattery 정적 메소드를 호출해도 아래와 같이 정상 출력 되는 것을 확인할 수 있습니다.
위 예제처럼 인터페이스는 구현 클래스에 공통적인 기능을 강제하면서, 상수와 정적 메소드, 디폴트 메소드 등을 활용해 유연하고 확장성 있는 설계를 가능하게 합니다. 인터페이스는 다형성을 높여줌으로써 다양한 객체를 하나의 인터페이스 타입으로 다룰 수 있어 유지보수성 및 코드 재사용성을 크게 향상시키는 중요한 도구입니다.
'Java' 카테고리의 다른 글
[Java] 제네릭이란? (0) | 2024.11.03 |
---|---|
[Java] final 필드, 메소드, 클래스 (0) | 2024.10.30 |
[Java] 추상 클래스란 무엇인가? (0) | 2024.10.30 |
[Java] 객체 지향 프로그래밍(OOP)의 4가지 특징과 원칙(SOLID) (0) | 2024.10.29 |
[Java] 자바 가상 머신(JVM)란 무엇인가? (2) | 2024.10.29 |