[Java 20] 자바 - 인터페이스(1)

학습목표

  1. 인터페이스의 역할과 추상 클래스와의 차이점을 이해한다.
  2. 인터페이스 선언과 구현방법을 정확히 이해한다.
  3. 인터페이스 사용 방법을 이해한다.

인터페이스 역할

인터페이스(Interface)는 객체의 사용 방법을 정의한 타입입니다. 인터페이스는 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할을 합니다.
인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역할을 합니다. 개발 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출시킵니다.

인터페이스

인터페이스를 사용함으로써 개발 코드를 수정하지 않고, 사용하는 객체를 변경할 수 있습니다. 마치 규격만 맞으면 다른 부품으로 바꿀 수 있는 것처럼 쉽게 변경이 가능합니다.

위의 그림과 같이 인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 달라질 수 있습니다. 개발 코드 측면에서 코드 변경없이 실행 내용과 리턴값을 다양화할 수 있다는 장점을 가지게 됩니다.


인터페이스 선언

인터페이스의 선언은 다음과 같이 합니다.

[ public ] interface 인터페이스명 {...}

클래스는 필드, 생성자, 메소드를 멤버로 가지는 반면, 인터페이스는 상수와 메소드만을 멤버로 가지게 됩니다. 인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없는 것입니다.

interface 인터페이스명 {
  // 상수
  타입 상수명 = ;
  // 추상 메소드
  타입 메소드명(매개변수, ...);
  // 디폴트 메소드
  default 타입 메소드명(매개변수, ...) {...}
  // 정적 메소드
  static 타입 메소드명(매개변수) {...}
}


상수 필드(Constant Field) 상수는 인터페이스에 고정된 값으로 런타임 시에 데이터를 바꿀 수 없습니다. 상수를 선언할 경우 반드시 초기값을 대입
추상 메소드(Abstract Method) 추상 메소드는 객체가 가지고 있는 메소드를 설명한 것으로 호출할 때 어떤 매개값이 필요하고, 리턴 타입이 무엇인지만 알려줍니다. 실제 실행부는 객체가 가짐
디폴트 메소드(Default Method) 디폴트 메소드는 인터페이스에 선언되지만 사실은 객체가 가지고 있는 인스턴스 메소드라고 생각
정적 메소드(Static Method) 객체가 없어도 인터페이스만으로 호출이 가능


상수 필드 선언

인터페이스는 데이터를 저장할 수 없기 때문에 데이터를 저장할 인스턴스 또는 정적 필드를 선언할 수 없고 대신 상수 필드만 선언이 가능합니다. 상수는 public static final로 선언하는데 인터페이스에 선언된 필드의 경우 이를 생략할 수 있습니다.

[ pulbic static final ] 타입 상수명 = ;
pulbic interface RemoteControl {
  public int MAX_VOLUME = 10;
  public int MIN_VOLUME = 0;
}


추상 메소드 선언

인터페이스를 통해 호출된 메소드는 객체에서 실행되기 때문에 인터페이스의 메소드는 실행 블록이 필요 없는 추상 메소드로 선언합니다. 추상 메소드는 리턴 타입, 메소드명, 매개변수만 기술되고 중괄호 {}를 붙이지 않는 메소드입니다.

public interface RemoteControl {
	// 상수
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 10;

	// 추상 메소드
	public void turnOn();
	public void turnOff();
	public void setVolume(int Volume);
}


디폴트 메소드와 정적 메소드 선언

public interface RemoteControl {
	// 상수
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 10;

	// 추상 메소드
	public void turnOn();

	public void turnOff();

	public void setVolume(int Volume);

	// 디폴트 메소드
	default void setMute(boolean mute) {
		if (mute) {
			System.out.println("무음 처리합니다.");
		} else {
			System.out.println("무음 해제합니다.");
		}
	}

  // 정적 메소드
	static void changeBettery() {
		System.out.println("건전지를 교환합니다.");
	}
}


인터페이스 구현

개발 코드가 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출합니다. 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 합니다. 이러한 객체를 인터페이스의 구현(implement) 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 합니다.

구현 클래스

구현 클래스는 일반 클래스와 동일하지만, 인터페이스 타입으로 사용할 수 있음을 나타내기 위해 클래스 선언부에 implements 키워드를 추가합니다.

public class 구현클래스명 implements 인터페이스명 {
  //인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}

구현 클래스에서 인터페이스의 추상 메소드들에 대한 실체 메소드를 작성할 때 주의할 점은 인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖기 때문에 public 보다 더 낮은 접근 제한으로 작성될 수 없습니다. 또한, 인터페이스에 선언된 추상 메소드에 대응하는 실체 메소드를 구현 클래스가 작성하지 않으면 구현 클래스는 자동적으로 추상 클래스가 됩니다. 그렇기 때문에 클래스 선언부에 abstract 키워드를 추가해야 합니다.

pulbic abstract class Television implements RemoteControl {
  // setVolume() 실체 메소드가 없다.
  public void turnOn() {...}
  public void turnOff() {...}
}

예제

[Television] 구현 클래스

public class Television implements RemoteControl {
	private int volume;

	// turnOn
	public void turnOn() {
		System.out.println("TV를 켭니다.");
	}

	// turnOff
	public void turnOff() {
		System.out.println("TV를 끕니다.");
	}

	// setVolume
	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);
	}
}

[Audio] 구현 클래스

public class Audio implements RemoteControl {
	private int volume;

	// turnOn
	public void turnOn() {
		System.out.println("Audio를 켭니다.");
	}

	// turnOff
	public void turnOff() {
		System.out.println("Audio를 끕니다.");
	}

	// setVolume
	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);
	}

}


익명 구현 객체

구현 클래스를 만들어 사용하는 것이 일반적이나 일회성의 구현 객체를 만들기 위해 소스 파일 만들고 클래스 선언하는 것은 비효율적으로 느껴집니다. 자바는 소스파일을 만들지 않고도 구현 객체를 만들 수 있는 방법을 제공하는데, 이것이 바로 익명 구현 객체입니다.

인터페이스 변수 = new 인터페이스() {
    //인터페이스에 선언된 추상 메소드의 실체 메소드 선언
};  // ;q반드시 붙여야함
public class RemoteControlMain

예제

public class RemoteControlMain {
	public static void main(String[] args) {
		RemoteControl rc = new RemoteControl() {
			public void turnOn() {
				System.out.println("1turnOn");
			}
			public void turnOff() {
				System.out.println("turnOff");
			}
			public void setVolume(int volume) {
				System.out.println("setVolume");
			}
		};
	}
}


다중 인터페이스 구현 클래스

인터페이스

인터페이스 A와 인터페이스 B가 객체의 메소드를 호출할 수 있으려면 객체는 이 두 인터페이스를 모두 구현해야 합니다.

public class 구현클래스명 implements 인터페이스A, 인터페이스B {
  //인터페이스 A에 선언된 추상 메소드의 실체 메소드 선언
  //인터페이스 B에 선언된 추상 메소드의 실체 메소드 선언
}


예제

public class SmartTV implements RemoteControl, Searchable {
	private int volume;

	// turnOn
	public void turnOn() {
		System.out.println("TV를 켭니다.");
	}

	// turnOff
	public void turnOff() {
		System.out.println("TV를 끕니다.");
	}

	// setVolume
	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);
	}

  // search
	public void search(String url) {
		System.out.println(url + "을 검색합니다.");
	}
}


인터페이스 사용

인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 합니다. 인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장합니다.

case 1

인터페이스 변수;
변수 = 구현객체;
RemoteControl rc;
rc = new Television();
rc = new Audio();

case 2

인터페이스 변수 = 구현객체;
RemoteControl rc = new Television();


예제

public class MyClass {

	RemoteControl rc = new Television();
	
	MyClass(RemoteControl rc) {   //RemoteControl rc는 MyClass mc = new MyClass(new Television());과 동일
		this.rc = rc;
	}
	
	void methodA() {
		RemoteControl rc = new Audio();
	}
	
	void methodB(RemoteControl rc) {} //RemoteControl rc는 mc.methodB(new Audio());와 동일
}


추상 메소드 사용

구현 객체가 인터페이스 타입에 대입되면 인터페이스에 선언된 추상 메소드를 개발 코드에서 호출이 가능합니다.

RemoteControl rc = new Television();
  rc.Television();
  rc.Audio();


예제

public class RemoteControlMain2 {
	public static void main(String[] args) {
		RemoteControl rc = null;
		rc = new Television();
		rc.turnOn();
		rc.turnOff();
		
		rc = new Audio();
		rc.turnOn();
		rc.turnOff();
	}
}

실행결과

TV를 켭니다.
TV를 끕니다.
Audio를 켭니다.
Audio를 끕니다.


디폴트 메소드 사용

디폴트 메소드는 인터페이스에 선언되지만, 추상 메소드가 아닌 인스턴스 메소드이기 때문에 구현 객체가 있어야만 사용이 가능합니다. RemoteControl이라는 인터페이스는 setMute()라는 디폴트 메소드를 가지고 있지만, 다음과 같이 호출할 수 없습니다.

RemoteControl.setMute(true);

setMute() 메소드를 호출하려면 RemoteControl의 구현 객체가 필요한데, 다음과 같이 Television 객체를 인터페이스 변수에 대입하고 나서 setMute() 메소드 호출이 가능합니다.

RemoteControl rc = new RemoteControl();
rc.setMute(true);


예제

public class Audio implements RemoteControl {
	private int volume;
	private boolean mute;

	// turnOn
	public void turnOn() {
		System.out.println("Audio를 켭니다.");
	}

	// turnOff
	public void turnOff() {
		System.out.println("Audio를 끕니다.");
	}

	// setVolume
	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);
	}
	
	//디폴트 메소드 재정의
	@Override
	public void setMute(boolean mute) {
		this.mute = mute;
		if(mute) {
			System.out.println("Audio 무음 처리합니다.");
		} else {
			System.out.println("Audio 무음 해제합니다.");
		}
	}
}

실행결과

TV를 켭니다.
무음 처리합니다.
Audio를 켭니다.
Audio 무음 처리합니다.


정적 메소드 사용

인터페이스 정적 메소드는 인터페이스에서 바로 호출이 가능합니다.

public class RemoteControlMain2 {
	public static void main(String[] args) {
		RemoteControl.changeBettery();
	}
}

태그:

카테고리:

업데이트:

댓글남기기