본문 바로가기

Java

[Java] 이것이 자바다 - 인터페이스

인터페이스란?

인터페이스는 보통 추상클래스와 많이 비교하는데 추상 클래스가 그 추상 클래스를 상속 받아서 추상 클래스에서 선언된 변수나 메소드를 실체 클래스에서 사용하고 확장시키는데 의의가 있다면 인터페이스는 인터페이스에서 선언한 추상 메소드를 자식클래스에서 구현을 강제함으로써 같은 동작을 보장하는데 의의가 있다. 만약 인터페이스에서 선언한 추상메소드를 구현 클래스에서 오버라이드 하지 않으면 컴파일 에러가 발생한다. 또한 인터페이스는 추상클래스와 달리 다중 상속이 가능하다.

인터페이스 구성

상수 필드(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("건전지를 교환합니다.");
	}
	
}

장비를 켜고 끌수 있는 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();
	}
    
}

인터페이스로 구현 객체를 사용하려면 위와 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 한다.

위와 같이 선언하고 실행하면 아래와 같은 결과를 얻을 수 있다.


여기까지는 인터페이스에서 선언한 추상 메소드를 구현하고 사용해 보았고 지금부터는 디폴트 메소드와 정적 메소드를 사용해 보겠다. 인터페이스 구성 문단에서 설명했듯이 디폴트 메소드는 인터페이스에서 실행부를 구현하지만 인터페이스에서 바로 사용할 수 없다. 때문에 추상 메소드와 마찬가지로 구현 객체가 필요하다.

먼저 추상 메소드의 실행부를 구현했던 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 정적 메소드를 호출해도 아래와 같이 정상 출력 되는 것을 확인할 수 있다.