이 게시글은 이것이 자바다(저자 : 신용권, 임경균)의 책과 동영상 강의를 참고하여 개인적으로 정리하는 글임을 알립니다.
디폴트(default) 메소드
인터페이스에 추상 메소드를 선언할 수 있다.
추상 메소드는 애초에 실행 블록 자체를 선언할 수 없기 때문에, 실행 코드를 넣을 수 없다.
하지만 디폴트 메소드는 실행 블록이 있기 때문에 실행 코드를 넣을 수 있다.
일반적으로 구현 클래스가 여러 개인데, 동일한 코드를 넣어야 할 때, 일일이 다 오버라이드하기 번거로울때 쓰인다.
- 인터페이스 안에 선언되는 메소드이므로 구현 객체가 반드시 필요
- 디폴트 메소드를 사용하면 구현 클래스에서 따로 정의할 필요가 없다.(오버라이드 하지 않으면 모든 객체에서 동일한 기능을 함)
- 구현 클래스에서 재정의(오버라이드)도 가능하다.(재정의시 public 접근 제한자를 붙여야하고, default 키워드를 생략해야 함.)
- 선언 방법은 클래스 메소드와 동일, default 키워드를 리턴 타입 앞에 붙여야 한다는 차이점이 있다.
public interface itf {
default void func() {...} //디폴트 메소드
}
- 디폴트 메소드의 실행부에는 상수 필드를 읽거나 추상 메소드를 호출하는 코드를 작성할 수 있다.
아래는 리모콘 인터페이스에 오디오(구현 객체)와 텔레비전(구현 객체)이 대입되는 예제이다.
리모콘 인터페이스에는 디폴트 메소드가 정의되어있고, 오디오 클래스에는 디폴트 메소드가 오버라이드 되어있다.
RemoteControl.java
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("무음 해제합니다.");
}
}
}
Audio.java
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 볼륨: " + volume);
}
//필드
private int memoryVolume;
//디폴트 메소드 재정의
@Override
public void setMute(boolean mute) {
if(mute) {
this.memoryVolume = this.volume;
System.out.println("무음 처리합니다.");
setVolume(RemoteControl.MIN_VOLUME);
} else {
System.out.println("무음 해제합니다.");
setVolume(this.memoryVolume);
}
}
}
Television.java
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 볼륨: " + volume);
}
}
RemoteControlExample.java
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);
System.out.println();
//Audio 객체를 생성하고 인터페이스 변수에 대입
rc = new Audio();
rc.turnOn();
rc.setVolume(5);
//디폴트 메소드 호출
rc.setMute(true);
rc.setMute(false);
}
}
/*
TV를 켭니다.
현재 TV 볼륨: 5
무음 처리합니다.
현재 TV 볼륨: 0
무음 해제합니다.
Audio를 켭니다.
현재 Audio 볼륨: 5
무음 처리합니다.
현재 Audio 볼륨: 0
무음 해제합니다.
현재 Audio 볼륨: 5
*/
정적(static) 메소드
- 인터페이스에도 정적 메소드 선언 가능
- 구현 객체가 없어도 인터페이스만으로 호출 가능
- 선언 방법은 클래스 정적 메소드와 완전 동일(단, public을 생략하더라도 자동으로 컴파일 과정에서 붙는다.)
- 인터페이스에 선언된 정적 메소드는 구현 객체 없이 인터페이스명으로 접근해서 호출할 수 있다.
아래의 예제는 리모콘 인터페이스에 있는 정적 메소드를 호출하는 예제이다.
RemoteControl.java
public interface RemoteControl {
//상수 필드
int MAX_VOLUME = 10;
int MIN_VOLUME = 0;
//추상 메소드
void turnOn();
void turnOff();
void setVolume(int volume);
//디폴트 메소드
default void setMute(boolean mute) {
//이전 예제와 동일한 코드이므로 생략
}
//정적 메소드
static void changeBattery() {
System.out.println("리모콘 건전지를 교환합니다.");
}
}
RemoteControlExample.java
public class RemoteControlExample {
public static void main(String[] args) {
//정적 메소드 호출
RemoteControl.changeBattery();
}
}
/*
리모콘 건전지를 교환합니다.
*/
프라이빗(private) 메소드
인터페이스의 상수 필드, 추상 메소드, 디폴트 메소드, 정적 메소드는 모두 public 접근 제한을 갖는다.
또한 인터페이스에 외부에서 접근할 수 없는 private 메소드 선언도 가능하다.
구분 |
설명 |
private 메소드 |
구현 객체가 필요한 메소드 |
private 정적(static) 메소드 |
구현 객체가 필요 없는 메소드 |
- private 메소드는 디폴트 메소드 안에서 호출이 가능
- private 정적 메소드는 디폴트 메소드 뿐만 아니라 정적 메소드 안에서도 호출이 가능
- 두 메소드의 용도는 디폴트와 정적 메소드들의 중복 코드를 줄이기 위함이다.
아래의 예제는 Service 인터페이스에서 2개의 디폴트 메소드와 2개의 정적 메소드 중 중복 코드 부분을 각각 private 메소드와 private 정적 메소드로 선언하고 호출하는 방법을 보여준다.
Serivce.java
public interface Service {
//디폴트 메소드
default void defaultMethod1() {
System.out.println("defaultMethod1 종속 코드");
defaultCommon();
}
default void defaultMethod2() {
System.out.println("defaultMethod2 종속 코드");
defaultCommon();
}
//private 메소드
private void defaultCommon() {
System.out.println("defaultMethod 중복 코드A");
System.out.println("defaultMethod 중복 코드B");
}
//정적 메소드
static void staticMethod1() {
System.out.println("staticMethod1 종속 코드");
staticCommon();
}
static void staticMethod2() {
System.out.println("staticMethod2 종속 코드");
staticCommon();
}
//private 정적 메소드
private static void staticCommon() {
System.out.println("staticMethod 중복 코드C");
System.out.println("staticMethod 중복 코드D");
}
}
ServiceImpl.java
public class ServiceImpl implements Service {
//Service 인터페이스에는 모드 디폴트, 정적, 프라이빗 메소드만 있으므로 아무것도 작성 안해도 된다.
}
ServiceExample.java
public class ServiceExample {
public static void main(String[] args) {
//인터페이스 변수 선언과 구현 객체 대입
Service service = new ServiceImpl();
//디폴트 메소드 호출
service.defaultMethod1();
System.out.println();
service.defaultMethod2();
System.out.println();
//정적 메소드 호출
Service.staticMethod1();
System.out.println();
Service.staticMethod2();
System.out.println();
}
}
/*
defaultMethod1 종속 코드
defaultMethod 중복 코드A
defaultMethod 중복 코드B
defaultMethod2 종속 코드
defaultMethod 중복 코드A
defaultMethod 중복 코드B
staticMethod1 종속 코드
staticMethod 중복 코드C
staticMethod 중복 코드D
staticMethod2 종속 코드
staticMethod 중복 코드C
staticMethod 중복 코드D
*/