Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 깊이우선탐색
- 항해
- 99클럽
- 너비우선탐색
- 백트래킹
- 싸피
- til
- ssafy
- dfs
- python
- java
- 개발자스터디
- 자바
- 구현
- 코딩테스트
- 파이썬
- 브루트포스
- 백준
- 삼성청년SW아카데미
- 99일지
- 프로그래머스
- DP
- 다이나믹프로그래밍
- programmers
- 알고리즘
- BOJ
- 위상정렬
- SSAFY수료식
- BFS
- 문자열
Archives
- Today
- Total
EunJng
[Java] 이것이 자바다 Ch.08 본문
'이것이 자바다' 교재 및 강의를 참고하여 정리한 내용입니다.
Ch.08 인터페이스
인터페이스 역할
- 사전적 의미: 두 장치를 연결하는 접속기
- 객체 A가 B를 직접 사용한다면 B가 C로 변경될 경우 A의 소스 코드를 B에서 C로 변경하는 작업이 필요하지만, 인터페이스를 사용하면 B가 C로 변경된 것에는 관심이 없다.
- 인터페이스는 다형성 구현에 주된 기술로 이용된다.
인터페이스와 구현 클래스 선언
인터페이스 선언
- class 키워드 대신 interface 키워드를 사용
- 중괄호 안에 인터페이스가 가지는 멤버들을 선언
public interface 인터페이스명 {
// public 상수 필드
// public 추상 메소드
// public 디폴트 메소드
// public 정적 메소드
// private 메소드
// private 정적 메소드
}
구현 클래스 선언
- 위 그림에서 객체 B를 인터페이스를 구현한 객체라 한다. 구현 객체는 인터페이스를 구현하고 있음을 선언부에 명시해야 한다
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를 켭니다.
}
}
상수 필드
- 인터페이스는 public static final 특성을 갖는 불변의 상수 필드를 멤버로 가질 수 있다.
- 인터페이스에 선언된 필드는 모두 public static final 특성을 갖기 때문에 public static final을 생략하더라도 자동적으로 컴파일 과정에서 붙게 된다.
- 상수명은 대문자로 작성하되, 언더바로 연결하는 것이 관례
- 상수는 구현 객체와 관련 없는 인터페이스 소속 멤버이므로 인터페이스로 바로 접근해서 상수값을 읽을 수 있다.
추상 메소드
- 인터페이스는 public 추상 메소드를 멤버로 가질 수 있다. public abstract 생략 가능
[public abstract] 리턴타입 메소드명(매개변수, ...);
- 구현 클래스에서 추상 메소드를 재정의할 때, 인터페이스의 추상 메소드는 기본적으로 public 접근 제한을 갖기 때문에 public보다 더 낮은 접근 제한으로 재정의할 수 없다.
디폴트 메소드
- 인터페이스에는 완전한 실행 코드를 가진 디폴트 메소드를 선언할 수 있다. 추상 메소드는 실행부({})가 없지만 디폴트 메소드는 실행부가 있고, default 키워드가 리턴 타입 앞에 붙는다.
[public] default 리턴타입 메소드명(매개변수, ...) {...}
- 디폴트 메소드는 구현 객체가 필요한 메소드로, 구현 객체를 인터페이스 변수에 대입하고 나서 호출해야 한다.
- 디폴트 메소드를 재정의할 때는 public 접근 제한자를 반드시 붙여야 하고, default 키워드를 생략해야 한다.
정적 메소드
- 정적 메소드는 구현 객체가 없어도 인터페이스만으로 호출 가능. public 생략 가능
[public | private] static 리턴타입 메소드명(매개변수, ...) {...}
- 정적 메소드의 실행부에는 상수 필드를 제외한 추상 메소드, 디폴트 메소드, private 메소드 등을 호출할 수 없다. 구현 객체가 필요한 인스턴스 메소드이기 때문
private 메소드
- private 메소드는 디폴트 메소드 안에서만 호출이 가능하지만, private 정적 메소드는 디폴트 메소드뿐 아니라 정적 메소드 안에서도 호출 가능
- 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();
}
}
다중 인터페이스 구현
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");
}
}
인터페이스 상속
- 클래스와 달리 인터페이스는 다중 상속 허용
public interface 자식인터페이스 extends 부모인터페이스1, 부모인터페이스2 {...}
- 자식 인터페이스의 구현 클래스는 자식 인터페이스의 메소드뿐만 아니라 부모 인터페이스의 모든 추상 메소드를 재정의해야 한다.
- 구현 객체가 자식 인터페이스 변수에 대입되면 자식 및 부모 인터페이스의 추상 메소드를 모두 호출할 수 있으나, 부모 인터페이스 변수에 대입되면 부모 인터페이스에 선언된 추상 메소드만 호출 가능
타입 변환
- 인터페이스의 타입 변환은 인터페이스와 구현 클래스 간에 발생
- 인터페이스 변수에 구현 객체를 대입하면 구현 객체는 인터페이스 타입으로 자동 타입 변환
- 인터페이스 타입을 구현 클래스 타입으로 변환시키려면 강제 타입 변환 필요
자동 타입 변환
인터페이스 변수 = 구현객체;
강제 타입 변환
구현클래스 변수 = (구현클래스) 인터페이스변수;
- 구현 객체가 인터페이스 타입으로 자동 변환되면, 인터페이스에 선언된 메소드만 사용 가능
RemoteControl rc = new Television();
rc.turnOn();
rc.turnOff();
rc.setVolume(5);
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();
- tire1과 tire2 필드에 어떠한 타이어 구현 객체가 대입되어도 Car 객체는 타이어 인터페이스에 선언된 메소드만 사용하므로 문제가 되지 않는다.
매개변수의 다형성
- 매개변수 타입을 인터페이스로 선언하면 메소드 호출 시 다양한 구현 객체를 대입할 수 있다.
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);
}
}
객체 타입 확인
- 메소드의 매개변수가 인터페이스 타입일 경우, 메소드 호출 시 매개값은 해당 인터페이스를 구현하는 모든 객체가 될 수 있다. 만약 매개값이 특정 구현 객체일 경우에만 강제 타입 변환을 하고 싶다면 instanceif 연산자를 사용해 매개값의 타입 검사
public void method( Vehicle vehicle) {
if(vehicle instanceof Bus) {
Bus bus = (Bus) vehicle;
// bus 변수 사용
}
}
// Java 12부터는 instanceof 연산의 결과가 true일 경우 우측 타입 변수를 사용할 수 있기 때문에 강제 타입 변환이 필요 없다.
if(vehicle instanceof Bus bus) {
// bus 변수 사용
}
봉인된 인터페이스
- Java 15부터는 무분별한 자식 인터페이스 생성을 방지하기 위해 봉인된 인터페이스 사용 가능
- Interface A의 자식 인터페이스는 InterfaceB만 가능하도록 InterfaceA를 봉인된 인터페이스로 선언
public sealed interface InterfaceA permits InterfaceB {...}
- InterfaceB는 non-sealed 키워드로 선언하거나, sealed 키워드를 사용해 또 다른 봉인 인터페이스로 선언해야 한다.
public non-sealed interface InterfaceB extends InterfaceA {...}
'STUDY > JAVA' 카테고리의 다른 글
[Java] 이것이 자바다 Ch.10 (0) | 2024.01.24 |
---|---|
[Java] 이것이 자바다 Ch.09 (0) | 2024.01.22 |
[Java] 이것이 자바다 Ch.07 (0) | 2024.01.13 |
[Java] 이것이 자바다 Ch.06 (0) | 2024.01.04 |
[Java] 이것이 자바다 Ch.05 (0) | 2023.12.30 |