JAVA 인터페이스
인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역활을 합니다. 개발 코드가 인터페이스의 메소드를 호출하면 인터페이스는 객체의 메소드를 호출 시킵니다. 그렇기 때문에 개발 코드는 객체의 내부 구조를 알 필요가 없고 인터페이스의 메소드만 알고 있으면 됩니다.
개발 코드를 직접 객체의 메소드에 호출하지 않고 중간에 인터페이스를 두는 이유는 개발코드를 수정하지 않고 사용하는 객체를 변경할 수 있도록 하기 위해서입니다. 인터페이스는 하나의 객체가아니라 여러 객체들과 사용이 가능하므로 어떤객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있습니다. 코드 변경 없이 실행 내용과 리턴값을 다양화 할 수 있다는 장점을 가지게 됩니다.
인터페이스 선언
인터페이스는 '~.java' 형태의 소스 파일로 작성되고 컴파일러(javac)를 통해 '~.class'형태로 컴파일 되기 때문에 물리적 형태는 클래스와 동일합니다. 그러나 소스를 작성할 때 선언하는 방법이 다릅니다.
인터페이스 선언은 class 키워드 대신에 interface 키워드를 사용합니다.
[public] interface 인터페이스이름 {...}
인터페이승 이름은 클래스 이름을 작성하는 방법과 동일합니다. 영어 대소문자를 구분하며, 첫 글자를 대문자로 하고 나머지는 소문자로 작성하는 것이 관례입니다.
클래스는 필드, 생성자, 메소드를 구성 멤버로 가지는데 비해, 인터페이스는 상수 필드와 추상메소드만을 구성 멤버로 가집니다. 인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없습니다.
interface 인터페이스 이름 {
//상수
타입 상수이름 = 값;
//추상 메소드
타입 메소드이름 (매개변수, ...);
상수 필드 선언
인터페이스는 객체 사용 방법을 정의한 것이므로 실행 시 데이터를 저장할 수 있는 인스턴스 또는 정적 필드를 선언할 수 없습니다. 그러나 상수 필드는 선언이 가능합니다. 단, 상수는 인터페이스에 고정된 값으로 실행 시에 데이터를 바꿀 수 없습니다.
인터페이스에 선언된 필드는 모두 public static final의 특성을 갖습니다. public static final를 생략하더라도 컴파일 과정에서 자동으로 붙게 됩니다.
[public static final] 타입 상수이름 = 값;
상수 이름은 대문자로 작성하되, 서로 다른 언어로 구성되어 있을 경우에는 언더바(_)로 연결하는 것이 관례입니다. 인터페이스 상수는 반드시 선언과 동시에 초기값을 지정해야 합니다.
상수 필드 선언
package ch08;
public interface RemoteControl {
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
}
추상 메소드 선언
인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행됩니다. 그렇기 때문에 인터페이스의 메소드는 실행 블록이 필요 없는 추상 메소드로 선언합니다. 추상 메소드는 리턴 타입, 메소드 이름, 매개 변수만 기술되고 중괄호{}를 붙이지 않는 메소드를 말합니다. 인터페이스에 선언된 추상 메소드는 모두 public abstract의 특성을 갖기 때문에 생략해도 커파일과정에서 자동으로 붙게됩니다.
[public abstract] 리턴타입 메소드이름(매개변수, ...);
추상 메소드 선언
package ch08;
public interface RemoteControl {
//상수
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
// 추상 메소드
public void turnOn();
public void turnOff();
public void setVolume(int volume);
//메소드 선언부만 작성
}
Remotecontrol 인터페이스에서 turnOn(), turnOff(), setVolume() 추상메소드를 선언했습니다. 3개의 메소드 리턴타입은 모두 void라는 것과 turnON(),turnOff() 메소드는 매개값이 필요없고 setVolume()만 int 매개값이 필요함을 알수 있습니다.
인터페이스 구현
개발 코드가 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출합니다. 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야합니다. 이러한 객체를 인터페이스의 구현객체라고 하고, 구현 객체를 생성하느 클래스를 구현 클래스라고 합니다.
구현클래스
구현 클래스는 보통의 클래스와 동일한데, 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 클래스 선언분에 implements 키워드를 추가하고 인터페이스 이름을 명시해야 합니다. 그리고 인터페이스에 선언된 추상 메소드의 실체 메소드를 선언해야 합니다.
public class 구현클래스이름 implements 인터페이스 이름 {
// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
구현 클래스
package ch08;
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);
}
}
Television과 Audio라는 이름을 가지고 있는 RemoteControl의 구현 클래스를 작성하는 방법을 보여줍니다. 클래스 선언부 끝에 implements remoteControl이 붙어 있기 때문에 이 두클래스는 RemoteControl 인터페이스로 사용이 가능합니다. RemoteControl에는 3개의 추상 매소드가 있기 때문에 Television Audio는 RemoteControl의 추상 메소드들에 대한 실체 메소드를 가지고 있어야 합니다.
구현클래스
package ch08;
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 연산자로 객체를 생성할 수 있습니다. 하지만 다음 코드는 인터페이스를 사용한 것이 아닙니다.
Television tv = new Television();
인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 합니다. 인터페이스 변수는 참조 타입이기 때문에 구현객체의 번지를 저장합니다.
인터페이스 변수;
변수 = 구현객체;
인터페이스 변수 = 구현객체
인터페이스 변수에 구현 객체 대입
package ch08;
public class RemoteControlExample {
public static void main(String[] args) {
RemoteControl re;
re = new Television();
re = new Audio();
}
}
다중 인터페이스 구현 클래스
객체는 다음 그림과 같이 다수의 인터페이스 타입으로 사용할 수 있습니다.
인터페이스 A와 인터페이스 B가 객체의 메소드 호출할 수 있으려면 객체는 이 두 인터페이스를 모두 구현해야합니다. 따라서 구현 클래스는 다음과 같이 작성되어야 합니다.
public class 구현클래스이름 implements 인터페이스A, 인터페이스B {
// 인터페이스 A에 선언된 추상메소드의 실체 메소드 선언
// 인터페이스 B에 선언된 추상메소드의 실체 메소드 선언
}
다중 인터페이스를 구현할 경우, 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성해야 합니다.
다음은 인터넷을 검색할 수 있는 인터페이스입니다.
인터페이스
package ch08;
public interface Searchable {
void search(String url);
}
다중 인터페이스 구현 클래스
package ch08;
public class SmartTelevision implements RemoteControl, Searchable {
private int volume;
public void turnOn() {
System.out.println("TV를 켭니다.");
}
public void turnOff() {
System.out.println("TV를 끕니다.");
}
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 void search(String url) {
System.out.println(url+"을 검색합니다.");
}
// Searchable 의 추상 메소드에 대한 실체메소드
}
인터페이스 변수에 구현 객체 대입
package ch08;
public class smarttelevisionExample {
public static void main(String[] args) {
SmartTelevision tv = new SmartTelevision();
RemoteControl rc = tv;
Searchable searchable = tv;
}
}
인터페이스 사용
구현 객체가 인터페이스 변수에 대입된다는 것을 알았으니, 인터페이스로 구현객체를 사용하는 방법을 알아보겠습니다.
클래스를 선언할 때 인터페이스는 필드, 생성자 또는 메소드의 매개 변수, 생성자 또는 메소드의 로컬 변수로 선언될 수 있습니다.
public class Myclass {
// 필드
/* 1 */RemoteControl rc = new Television(); // 생성자의 매개값으로 구현 객체 타입
// 생성자
/* 2 */MyClass(RemoteControl rc) {
this.rc = rc;
// 메소드
void methodA() {
// 로컬 변수
/* 3 */RemoteControl rc =new Audio();
}
/* 4 */void methodB (RemoteControl rc) {...} // 생성자의 매개값으로 구현 객체 타입
}
1. 인터페이스가 필드 타입으로 사용될 경우, 필드에 구현 객체를 대입할 수 있습니다.
2. 인터페이스가 생성자의 매개 변수 타입으로 사용될 경우 new 연산자로 객체를 생성할 때 구현 객체를 생성자의 매개값으로 대입할 수 있습니다.
3. 인터페이스가 로컬 변수 타입으로 사용될 경우, 변수에 구현 객체를 대입할 수 있습니다.
4. 인터페이스가 메소드의 매개 변수 타입으로 사용될 경우, 메소드 호출 시 구현객체를 매개값으로 대입할 수 있습니다.
구현 객체가 인터페이스 타입에 대입되면 인터페이스에 선언된 추상 메소드를 개발 코드에서 사용할 수 있게 됩니다.
개발 코드에서 RemoteControl의 변수로 rc로 turnOn() 또는 turnOff()메소드를 호출하면 구현 객체의 turnOn()과 turnOff() 메소드가 자동 실행됩니다.
1. 필드로 선언된 rc는 다음과 같이 사용될 수 있습니다.
MyClass myClass = new MyClass();
myClass.rc.turnOn(); // Television turnOn()이 실행
myClass.rc.setVolume(5); // Television turnOff()이 실행
2. 생성자의 매개 변수 타입으로 선언된 rc는 다음과 같이 선언할 수 있습니다.
MyClass( RemoteControl rc) {
this.rc =rc;
rc.turnOn();
rc.setVolume(5);
}
다음과 같이 MyClass 객체가 생성되었을 경우 Audio의 turnOn()과 setVolume() 메소드가 실행됩니다.
MyClass myClass = new MyClass(new Audio());
3. 로컬 변수로 선언된 rc는 다음과 같이 사용될 수 있습니다.
void methodA() {
RemoteControl rc =new Audio();
rc.turnOn(); // Audio의 turnOn()이 실행
rc.setVolume(5); // Audio의 setvolume(5)가 실행
4. 메소드의 매개 변수 타입으로 선언된 rc는 다음과 같이 사용될 수 있습니다.
void methodB(RemoteCOntrol rc) {
rc.turnOn();
rc.setVolume(5);
}
다음과 같이 methodB() 메소드가 호출되었을 경우에는 Television의 turnOn()과 setvolume()메소드가 실행됩니다.
MyClass myClass = new MyCalss()
myClass.methodB(new Television());
지금 까지의 내용을 예제로 만들어 보겠습니다.
인터페이스 사용
package ch08;
public class MyClass {
//필드
RemoteControl rc = new Television();
//생성자
MyClass() {
}
MyClass(RemoteControl rc){
this.rc = rc;
rc.turnOn();
rc.setVolume(5);
}
//메소드
void methodA() {
RemoteControl rc = new Audio();
rc.turnOn();
rc.setVolume(5);
}
void methodB(RemoteControl rc) {
rc.turnOn();
rc.setVolume(5);
}
}
인터페이스 사용
package ch08;
public class MyClassExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println("1)---------------------");
MyClass myClass1 = new MyClass();
myClass1.rc.turnOn();
myClass1.rc.setVolume(5);
System.out.println("2)---------------------");
MyClass myClass2 = new MyClass(new Audio());
System.out.println("3)---------------------");
MyClass myClass3 = new MyClass();
myClass3.methodA();
System.out.println("4)---------------------");
MyClass myClass4 = new MyClass();
myClass4.methodB(new Television());
}
}