인터페이스란?
두 장치를 연결하는 접속기
- 만약 객체 A가 객체 B를 직접 사용한다면 객체 A의 소스 코드를 객체 B에서 C로 변경하는 작업 필요
- 그러나 객체 A가 인터페이스의 메소드를 호출한다면 객체 B가 C로 변경된 것에는 신경쓸 필요 없음
- 즉, 객체 A는 신경쓸 필요 없이 인터페이스만 고치면 된다.
인터페이스 선언 - interface
public interface 인터페이스명 {
// public 상수 필드
// public 추상 메소드(구현 클래스에서 구현해야 함)
// public 디폴트 메소드(default)
// public 정적 메소드
// private 메소드
// private 정적 메소드(static)
}
구현 클래스 선언 - implements
public class B implements 인터페이스명 {...}
변수 선언과 구현 객체 대입
- 인터페이스도 하나의 타입이므로 변수의 타입으로 사용할 수 있다.
- 인터페이스는 참조 타입에 속하므로 인터페이스 변수에는 객체를 참조하고 있지 않다는 뜻으로 null 대입 가능하다.
- 인터페이스를 통해 구현 객체를 사용하려면 인터페이스 변수에 구현 객체의 번지를 대입해야 한다.
public interface RemoteControl {
// public 추상 메소드
public void turnOn();
}
public class Television implements RemoteControl {
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}
}
public class Audio implements RemoteControl {
@Override
public void turnOn() {
System.out.println("Audio를 켭니다.");
}
}
public class RemoteControlExample {
public static void main(String[] args) {
// 변수 타입으로 인터페이스 선언
RemoteControl rc;
// rc 변수(인터페이스)에 Television 객체 대입
rc = new Television();
rc.turnOn(); // TV를 켭니다.
// rc 변수에 Audio 객체를 대입(교체)
rc = new Audio();
rc.turnOn(); // Audio를 켭니다.
}
}
상수 필드 - static final
- 인터페이스에 선언된 필드는 모두
public static final
특성을 갖기 때문에public static final
을 생략하더라도 자동적으로 컴파일 과정에서 붙게 된다. - 상수명은 대문자로하되, 서로 다운 단어는 _(언더바)로 연결
- 상수 필드는 객체 없이 클래스로 접근해서 읽을 수 있음
public interface 인터페이스이름 {
int MAX_VOLUME = 10;
}
추상 메소드 - abstract
- 인터페이스에서는
public abstract
를 생략해도 컴파일 과정에서 자동으로 붙게 됨[public abstract] 리턴타입 메소드명(매개변수, ...);
- 구현 클래스에서 추상 메소드를 재정의할 때, 인터페이스의 추상 메소드는 기본적으로 public 접근 제한을 갖기 때문에 public보다 더 낮은 접근 제한으로 재정의할 수 없다
디폴트 메소드 - default
- 인터페이스에 완전한 실행 코드를 가진 메소드
- 추상 메소드와 다르게 실행부가 구현되어 있음
[public] default 리턴타입 메소드명(매개변수, ...) {...}
- 구현 객체가 필요한 메소드
- 구현 객체를 인터페이스 변수에 대입하고 나서 호출
- 디폴트 메소드를 재정의할 때는 public 접근 제한자를 반드시 붙여야 하고, default 키워드를 생략
정적 메소드 - static
- 구현 객체 없이 인터페이스만으로 접근하여 호출 가능
- [public | private] static 리턴타입 메소드명(매개변수, ...) {...}
- 정적 메소드의 실행부에는 상수 필드를 제외한 추상 메소드, 디폴트 메소드, private 메소드 등을 호출할 수 없다.
- 구현 객체가 필요한 인스턴스 메소드이기 때문이다.
public interface RemoteControl {
// 상수 필드
int MAX_VOLUME = 10;
int MIN_VOLUME = 0;
// 추상 메소드
void turnOn();
void turnOff();
void setVolume(int volume);
// 디폴트 인스턴스 메소드
default void setMute(boolean mute) {
if(mute) {
System.out.println("무음 처리합니다.");
setVolume(MIN_VOLUME);
} else {
System.out.println("무음 해제합니다.");
}
}
// 정적 메소드
static void changeBattery() {
System.out.println("리모콘 건전지를 교환합니다.");
}
}
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);
}
}
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);
}
// 필드
privat int memoryVolume;
// 디폴트 메소드 재정의
@Override
public void setMute(boolean mute) {
if(mute) {
this.memoryVolume = this.volume;
setVolume(RemoteControl.MIN_VOLUME);
} else {
setVolume(this.memoryVolume); // 원래 볼륨으로 복원
}
}
}
public class RemoteControlExample {
public static void main(String[] args) {
// 인터페이스 변수 선언
RemoteControl rc;
// Television 객체를 생성하고 인터페이스 변수에 대입
rc = new Television();
rc.turnOn();
rc.setVolume(5);
// 디폴트 메소드 호출
rc.setMute(true);
rc.setMute(false);
// Audio 객체를 생성하고 인터페이스 변수에 대입
rc = new Audio();
rc.turnOn();
rc.setVolume(5);
// 디폴트 메소드 호출
rc.setMute(true);
rc.setMute(false);
// ★ 구현 객체 없이 인터페이스 명으로 정적 메소드 호출 ★
RemoteControl.changeBattery();
}
}
private 메소드
- private 메소드
- 디폴트 메소드 안에서만 호출 가능
- private static 메소드
- 디폴트 메소드와 static 메소드에서 호출 가능
// static 메소드에서 private static 메소드 호출
static void staticMethod1() {
staticCommon();
}
private static void staticCommon() {
...
}
다중 인터페이스
- 구현 객체는 여러 개의 인터페이스 implements 할 수 있다.
public class 구현클래스명 implements 인터페이스A, 인터페이스B { //모든 추상 메소드 재정의 }
public interface RemoteControl {
// 추상 메소드
void turnOn();
void turnOff();
}
public interface Searchable {
// 추상 메소드
void search(String url);
}
public class SmartTelevision implements RemoteControl, Searchable {
// turnOn() 추상 메소드 오버라이딩
@Override
public void turnOn() {
System.out.println("TV를 켭니다.");
}
// turnOff() 추상 메소드 오버라이딩
@Override
public void turnOff() {
System.out.println("TV를 끕니다.");
}
// search() 추상 메소드 오버라이딩
@Override
public void search(String url) {
System.out.println(url + "을 검색합니다.");
}
}
public class MultiInterfaceImplExample {
public static void main(String[] args) {
// RemoteControl 인터페이스 변수 선언 및 구현 객체 대입
RemoteControl rc = new SmartTelevision();
// RemoteControl 인터페이스에 선언된 추상 메소드만 호출 가능
rc.turnOn();
rc.turnOff();
// Searchable 인터페이스 변수 선언 및 구현 객체 대입
Searchable searchable = new SmartTelevision();
// Searchable 인터페이스에 선언된 추상 메소드만 호출 가능
searchable.search("https://www.youtube.com");
}
}
인터페이스 상속 - extends
- 인터페이스도 다른 인터페이스 상속할 수 있음
- 다중 상속 가능
- 클래스는 단일 상속만 가능
public interface 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2 {...}
- 자식 인터페이스의 구현 클래스는 자식 인터페이스의 메소드뿐만 아니라 부모 인터페이스의 모든 추상 메소드를 재정의해야 한다.
- 부모 인터페이스1
- 부모 인터페이스2
- 자식 인터페이스 -> 얘 구현 클래스는 자식, 부모1, 부모2 추상 메소드 모두 구현
타입 변환
- 인터페이스 타입 변환은 인터페이스와 구현 클래스 간에 발생
자동 타입 변환
인터페이스 변수 = 구현객체;
- 구현 객체가 인터페이스 타입으로 자동 타입 변환
강제 타입 변환
구현클래스 변수 = (구현클래스) 인터페이스변수;
- RemoteControl로 자동 타입 변환된 Television 객체 rc
- Television 객체에만 선언된 setTime(), record() 메소드를 사용하려면 다시 Television으로 강제 타입 변환을 해야 함
RemoteControl rc = new Television();
rc.turnOn();
rc.turnOff();
rc.setVolume(5);
tv.setTime(); // 불가능
tv.record(); // 불가능
// Television으로 강제 타입 변환
Television tv = (Television) rc;
tv.turnOn();
tv.turnOff();
tv.setVolume(5);
tv.setTime();
tv.record();
필드의 다형성
public class Car {
// 필드로 인터페이스 선언하고 구현 객체 대입
Tire tire1 = new HankookTire();
Tire tire2 = new KumhoTire();
}
Car myCar = new Car();
myCar.tire1 = new KumhoTire(); // 구현 객체 변경 가능
매개변수의 다형성
- 매개변수 타입으로 인터페이스로 선언하면 인자로 다양한 구현 객체 대입 가능
- 자동 타입 변환 성질 이용
public interface Vehicle {
// 추상 메소드
void run();
}
public class Driver {
// ★ 구현 객체가 대입될 수 있도록 매개변수를 인터페이스 타입으로 선언 ★
void drive(Vehicle vehicle) {
vehicle.run(); // 인터페이스의 추상 메소드 호출
}
}
public class Bus implements Vehicle {
// 추상 메소드 재정의
@Override
public void run() {
System.out.println("버스가 달립니다.");
}
}
public class Taxi implements Vehicle {
// 추상 메소드 재정의
@Override
public void run() {
System.out.println("택시가 달립니다.");
}
}
public class DriverExample {
public static void main(String[] args) {
// Driver 객체 생성
Driver driver = new Driver();
// Vehicle 구현 객체 생성
Bus bus = new Bus();
Taxi taxi = new Taxi();
// ★ 매개값으로 구현 객체 대입 ★
driver.drive(bus);
driver.drive(taxi);
}
}
객체 타입 확인 - instanceof
- 상속에서 객체 타입을 위해 instanceof 연산자를 사용했듯이 인터페이스에서도 사용할 수 있다.
- 만약 매개값이 특정 구현 객체일 경우에만 강제 타입 변환을 하고 싶다면 instanceif 연산자를 사용해 매개값의 타입 검사
public void method(Vehicle vehicle) {
// vehicle이 Vehicle 타입의 Bus 객체일 경우 Bus 타입으로 강제 타입 변환
if(vehicle instanceof Bus) {
Bus bus = (Bus) vehicle;
// bus 변수 사용
}
}
// Java 12부터는 instanceof 연산의 결과가 true일 경우 우측 타입 변수를 사용할 수 있기 때문에 강제 타입 변환이 필요 없다.
if(vehicle instanceof Bus bus) {
// bus 변수 사용
}
봉인된 인터페이스
- Java 15부터는 무분별한 자식 인터페이스 생성을 방지하기 위해 봉인된 인터페이스 사용 가능
// Interface A의 자식 인터페이스는 InterfaceB만 가능
public sealed interface InterfaceA permits InterfaceB {...}
// InterfaceB는 non-sealed 키워드로 선언하거나, sealed 키워드를 사용해 또 다른 봉인 인터페이스로 선언
public non-sealed interface InterfaceB extends InterfaceA {...}
// InterfaceB는 non-sealed이기 때문에 상속 가능
public interface InterfaceC extends InterfaceB {...}
'Programming Language > 자바(JAVA)' 카테고리의 다른 글
[JAVA] java.base 모듈 - 각종 클래스, 리플렉션, 어노테이션 (1) | 2024.11.26 |
---|---|
[JAVA] 중첩 클래스, 중첩 인터페이스, 익명 객체 (0) | 2024.11.25 |
[JAVA] 상속 (0) | 2024.11.25 |
[JAVA] 클래스 - 접근 제어자, final, static (0) | 2024.11.24 |
[JAVA] 참조 타입 - 문자열, 배열 (3) | 2024.11.21 |