Java Category/Java

[JAVA] 인터페이스(Interface)

ReBugs 2023. 1. 10.

본 게시글은 혼자 공부하는 자바 (저자 : 신용권)의 책과 유튜브 영상을 참고하였고, 개인적으로 정리하는 글임을 알립니다.


자바에서 인터페이스는 객체의 사용 방법을 정의한 타입이다. 인터페이스를 통해 다양한 객체를 동일한 사용 방법으로 이용할 수 있다.

인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역할을 한다. 개발 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출시킨다. 그렇기 때문에 개발 코드는 객체의 내부 구조를 알 필요가 없고 인터페이스의 메소드만 알고 있으면 된다.

개발 코드가 직접 객체의 메소드를 호출하면 간단한데 왜 중간에 인터페이스를 두는지 의문점이 생긴다. 그 이유는 개발 코드를 수정하지 않고 사용하는 객체를 변경할 수 있도록 하기 위해서이다. 인터페이스는 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있다. 따라서 개발 코드 측면에서는 코드 변경 없이 실행 내용과 리턴값을 다양화할 수 있다는 장점을 가지게 된다.

 

인터페이스는 *.java 형태의 소스 파일로 작성되고 컴파일러를 통해 *.class 형태로 컴파일되기 때문에 물리적 형태는 클래스와 동일하다.

 

인터페이스 선언

패키지에 클래스를 추가하는 것과 같이 패키지에 인터페이스를 추가해준다.

RemoteControl이라는 이름을 가진 인터페이스를 선언하는 방법은 아래와 같다.

public interface RemoteControl {

}

 

  • 클래스는 필드, 생성자, 메소드를 멤버로 가질 수 있지만, 인터페이스는 상수 필드와 추상 메소드만을 멤버로 가질 수 있다.
  • 인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없다.
  • 인터페이스는 객체 사용 방법을 정의한 것이므로 실행 시 데이터를 저장할 수 있는 인스턴스 또는 정적 필드를 선언할 수 없다. 그러나 상수 필드는 선언이 가능하다.
  • 인터페이스에 선언된 필드는 모두 상수의 특징을 가지므로 public static final을 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.
  • 인터페이스 상수는 반드시 선언과 동시에 초기값을 지정해야한다.
상수 이름은 대문자로 작성하되, 서로 다른 단어로 구성되어 있을 경우에는 언더바로 연결하는 것이 관례이다.

인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행된다. 그렇기 때문에 인터페이스의 메소드는 추상 메소드로 선언한다. 인터페이스에 선언된 메소드는 모두 추상 메소드의 특징을 가지므로 public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.

public interface RemoteControl {
	//상수(public static final 생략 가능)
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 0;
	
	//추상 메소드(public abstract 생략 가능)
	void turnOn();
	void turnOff();
	void setVolume(int volume);
}

 

인터페이스 구현

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

 

구현 클래스는 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 클래스 선언부에 implements 키워드를 추가하고 인터페이스 이름을 명시해야 한다. 그리고 인터페이스에 선언된 추상 메소드의 실체 메소드를 선언해야 한다.

인터페이스 RemoteControl의 구현 클래스 이름이 Television이면 아래와 같이 선언한다.

public class Television implements RemoteControl{

}

 

아래의 코드들은 리모콘을 인터페이스로, 구현 객체를 TV와 스피커로 구현한 것이다.

 

RemoteControl.java(인터페이스)

package TestPackage;
public interface RemoteControl {
	//상수(public static final 생략 가능)
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 0;
	
	//추상 메소드(public abstract 생략 가능)
	void turnOn();
	void turnOff();
	void setVolume(int volume);
}

 

Television.java(구현 클래스)

package TestPackage;
public class Television implements RemoteControl{
	private int volume;
	
	//turnOn() 추상 메소드의 실체 메소드
	public void turnOn() {
		System.out.println("TV ON");
	}	
	//turnOff() 추상 메소드의 실체 메소드
	public void turnOff() {
		System.out.println("TV OFF");
	}
	//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);
	}
}

 

Speaker.java

package TestPackage;
public class Speaker implements RemoteControl{
		private int volume;
		
		//turnOn() 추상 메소드의 실체 메소드
		public void turnOn() {
			System.out.println("Speaker ON");
		}	
		//turnOff() 추상 메소드의 실체 메소드
		public void turnOff() {
			System.out.println("Speaker OFF");
		}
		//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("현재 Speaker 볼륨: " + this.volume);
		}
}

 

Test.java

package TestPackage;
public class Test {
	public static void main(String[] args) {
		RemoteControl rc;
		rc = new Television();
		rc.turnOn();
		rc.setVolume(5);
		rc.turnOff();
		
		rc = new Speaker();
		rc.turnOn();
		rc.setVolume(5);
		rc.turnOff();
	}
}
/*
TV ON
현재 TV 볼륨: 5
TV OFF
Speaker ON
현재 Speaker 볼륨: 5
Speaker OFF
*/

보는 것과 같이 인터페이스 변수에 어떤 객체가 들어가느냐에 따라서 실행결과가 달라짐을 볼 수 있다.

public을 생략하면 컴파일 에러
인터페이스의 모든 멤버는 기본적으로 public 접근 제한을 갖기 때문에 구현 클래스에서는 public보다 더 강한 접근제한으로 작성할 수 없다.
따라서 public을 생략하면 default 접근제한을 갖기 때문에 컴파일 에러가 뜬다.

 

다중 인터페이스

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

다중 인터페이스를 구현할 경우, 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성해야 한다.

따라서 구현 클래스는 다음과 같이 작성되어야 한다.

public class 구현클래스 implements 인터페이스A, 인터페이스B{

	//인터페이스 A에 선언된 추상 메소드의 실체 메소드 선언
    //인터페이스 B에 선언된 추상 메소드의 실체 메소드 선언
    
}

 

아래 코드는 리모콘 인터페이스와 인터넷 검색을 가능하게 해주는 인터페이스, 방금 나열한 인터페이스들을 구현한 스마트 TV 객체 코드이다.

Seachable.java

package TestPackage;
public interface Searchable {
	void search(String url);
}

 

RemoteControl.java

package TestPackage;
public interface RemoteControl {
	//상수(public static final 생략 가능)
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 0;
	
	//추상 메소드(public abstract 생략 가능)
	void turnOn();
	void turnOff();
	void setVolume(int volume);
}

 

SmartTelevision.java

package TestPackage;
public class SmartTelevision implements RemoteControl, Searchable{
	private int volume;
	//RemoteControl 인터페이스 메소드
	public void turnOn() {
		System.out.println("TV ON");
	}	
	public void turnOff() {
		System.out.println("TV OFF");
	}
	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);
	}
	//Searchable 인터페이스 메소드
	public void search(String url) {
		System.out.println(url + "을 검색");
	}
}

 

Test.java

package TestPackage;
public class Test {
	public static void main(String[] args) {
		SmartTelevision tv = new SmartTelevision();
		RemoteControl rc = tv;
		Searchable searchable = tv;
		tv.turnOn();
		tv.turnOff();
		tv.setVolume(5);
		tv.search("google.com");
	}
}
/*
TV ON
TV OFF
현재 TV 볼륨: 5
google.com을 검색
*/

tv객체는 RemoteControl, Searchable 인터페이스에서 선언한 추상 메소드를 모두 실행할 수 있는 것을 볼 수 있다.

 

인터페이스 사용

  • 인터페이스가 필드 타입으로 사용될 경우, 필드에 구현 객체를 대입할 수 있다.
  • 인터페이스가 생성자의 매개 변수 타입으로 사용될 경우, new 연산자로 객체를 생성할 때 구현 객체를 생성자의 매개값으로 대입할 수 있다.
  • 인터페이스가 로컬 변수 타입으로 사용될 경우, 변수에 구현 객체를 대입할 수 있다.
  • 인터페이스가 메소드의 매개 변수 타입으로 사용될 경우, 메소드 호출 시 구현 객체를 매개값으로 대입할 수 있다.

RemoteControl.java

package TestPackage;
public interface RemoteControl {
	//상수(public static final 생략 가능)
	int MAX_VOLUME = 10;
	int MIN_VOLUME = 0;
	
	//추상 메소드(public abstract 생략 가능)
	void turnOn();
	void turnOff();
	void setVolume(int volume);
}

 

Television.java

package TestPackage;
public class Television implements RemoteControl{
	private int volume;
	
	public void turnOn() {
		System.out.println("TV ON");
	}	
	public void turnOff() {
		System.out.println("TV OFF");
	}
	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);
	}
}

 

Speaker.java

package TestPackage;
public class Speaker implements RemoteControl{
	private int volume;
	public void turnOn() {
		System.out.println("Speaker ON");
	}	
	public void turnOff() {
		System.out.println("Speaker OFF");
	}
	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("현재 Speaker 볼륨: " + this.volume);
	}
}

 

MyClass.java

package TestPackage;
public class MyClass {
	// 필드
	RemoteControl rc = new Television();

	// 생성자
	MyClass() {
	}

	MyClass(RemoteControl rc) { //생성자의 매개값으로 구현 객체 대입
		this.rc = rc;
		rc.turnOn();
		rc.setVolume(5);
	}

	// 메소드
	void methodA() {
		RemoteControl rc = new Speaker();
		rc.turnOn();
		rc.setVolume(5);
	}

	void methodB(RemoteControl rc) { //생성자의 매개값으로 구현 객체 대입
		rc.turnOn();
		rc.setVolume(5);
	}
}

 

Test.java

package TestPackage;
public class Test {
	public static void main(String[] args) {
		System.out.println("1)----------------");
		
		MyClass myClass1 = new MyClass();
		myClass1.rc.turnOn();
		myClass1.rc.setVolume(5);
		
		System.out.println("2)----------------");
		
		MyClass myClass2 = new MyClass(new Speaker());
		
		System.out.println("3)----------------");
		
		MyClass myClass3 = new MyClass();
		myClass3.methodA();
		
		System.out.println("4)----------------");
		
		MyClass myClass4 = new MyClass();
		myClass4.methodB(new Television());
	}
}
/*
1)----------------
TV ON
현재 TV 볼륨: 5
2)----------------
Speaker ON
현재 Speaker 볼륨: 5
3)----------------
Speaker ON
현재 Speaker 볼륨: 5
4)----------------
TV ON
현재 TV 볼륨: 5
*/

 

댓글