본문 바로가기

Java

인터페이스(Interface)2

구현

- 인터페이스는 

  

-->(메소드 호출)                            -->(메소드 호출)

개발코드     <--(리턴값)                인터페이스      <--(리턴값)            객체


이런 모양이라고 앞에서 보았다. 

개발 코드가 메소드를 호출하면 인터페이스에서 객체의 메소드를 호출한다. 객체는 인터페이스에서 정의된 추상 메소드와 동일한 이름, 매개변수, 타입 을 가지고 실행코드를 가진 메소드라야한다. 간단히 말해서 추상메소드를 실행시킬 수 있는 재정의(Override)된 메소드를 가지고 있어야하는것이다. 

이 객체를 인터페이스의 구현(Implement)객체라고 하고 구현객체를 생성하는것이 구현 클래스이다. 

구현클래스의 모양은 보통의 클래스와 동일하다 하지만 인터페이스의 구현클래스 임을 알 수있도록 클래스명 뒤에 

public class 구현클래스명 implement 인터페이스명{

//인터페이스의 추상메소드의 실제메소드 선언

}    << 이런 모양으로 쓴다. 

그저 인터페이스의 구현클래스다 라는 표시가아니라 인터페이스 타입으로 사용할수있다는 소리.

그리고 안쪽에서는 인터페이스에서 추상메소드로 선언된 메소드의 실제 메소드를 선언한다  코드로 살펴 보자


public interface RemoteControl {

int MAX_VOLUME = 10;

int MIN_VOLUME = 0 ;

void turnOn();

void turnOff();

void setVolume(int volume);

}


RemoteControl 이라는 인터페이스가 있다. 여기에는 3개의 추상 메소드가 선언되있다. 
이 추상메소드를 구현하는 구현 클래스를 만들어보자


public class Television implement RemoteControl{
//필드
private int volume;

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

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


public void setVolume(int volume) {
//볼륨에 상수를 대입해서 Volume필드의 값을 제한한다.
if(volume > RemoteControl.MAX_VOLUME) {
//볼륨이 RemoteControl의 상수보다 크면 상수를 집어넣는다
this.volume = RemoteControl.MAX_VOLUME;

}else if(volume < RemoteControl.MIN_VOLUME){
//볼륨이 RemoteControl의 상수보다 작으면 상수를 집어넣는다
this.volume = RemoteControl.MIN_VOLUME;

}else {
this.volume = volume;
}
System.out.println("현재 볼륨 : "+volume);
}

이런 식이다 인터페이스의 구현클래스를 만들고 그안에서 인터페이스에 있는 추상메소드들의 구현메소드를 만드는것.

주의할 점은 인터페이스의 경우 모든 메소드의 기본 특성이 public 이다. 구현메소드에서 그것보다 더 낮은 접근 제한자로 바꿀수 없다.

만약 인터페이스처럼 public 를 생략하면 에러가 난다.

그리고 구현클래스에서 조차 추상클래스를 구현하지 않고 추상클래스로 만들때 구현클래스는 자동으로 추상클래스가 되므로 class앞에 abstract 를 붙여준다.


public abstract class Television implement RemoteControl {

// setVolume을 구현하지않고 일부만 구현했다. 구현클래스에서는 전부 구현해야한다. 

public void turnOn(){....}

public void turnOff(){....}

}


앞에 말했듯이 인터페이스를 사용하는 목적도 다형성을 위해서이다. 

앞에했던 추상클래스와 상속, 오버라이딩과 다르지않다. 구현 메소드위에 @Override 어노테이션을 사용하는 것도 가능하다.

어노테이션을 사용하면 인터페이스의 추상메소드에 대한 정확한 구현메소드인지 체크해준다.

구현클래스가 만들어지면 객체를 생성 할 수있다. 문제는 어떤 타입으로 작성하느냐 이다. 그냥 항상 객체만들듯이

구현클래스타입 변수명  = new 구현클래스타입();

이게 아니다. 

인터페이스로 구현 객체를 사용한다. 그러기 위해서는 인터페이스 타입의 변수를 선언하고 구현객체를 대입한다.

인터페이스타입 변수;     l    인터페이스타입 변수 = new 구현객체();

변수 = 구현객체;          

어디서 본것같다. 바로 상소에서 보았던 자식타입을 부모로 자동타입변환 시키는 것과 비슷하다.


만약 위의 인터페이스 RemoteControl로 구현객체인 Television을 사용하려면??

public class RemoteControlExample{

public static void main(String[]args){

RemoteControl rc;

rc = new Television();

}  

}

이런방법으로 인터페이스 타입 변수를 만들고 거기에 구현객체를 대입한다. 왜? 그건 뒤에서 알아보자


익명구현객체

구현객체를 보자 다형성으로 인해 언제든 교체가능한 객체가 구현객체다. 만약 교체시기가 아주 짧다면??
소모성이 강하다면?? 소모성이 강한 객체를 위해 소스파일을 만들고 클래스를 선언한다는것은 비효율적이다.

이때 우리 위대한 자바동무는 소스파일을 만들지않고 구현객체를 만들수 있게 해주는데 이것이 바로 익명구현객체이다.  자바의 UI프로그래밍에서의 이벤트 처리, 임시 작업스레드를 만들기 위해 익명 구현객체를 많이 활용한다고 한다.

특히 자바 8의 람다식에서 인터페이스의 익명구현객체를 만들기때문에 잘 익혀두는것이 좋다고한다.

익명구현객체를 생성후 인터페이스변수에 대입하는 코드는

인터페이스 변수 = new 인터페이스(){

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

}; << 실행문 이기때문에 세미콜론을 붙인다.

new 인터페이스() << 이걸로는 객체 생성이 불가능하다 인터페이스는 생성자가 없기때문이다.

뒤의 중괄호{ } 는 클래스를 선언하겠다는 뜻이다. 이름이없는 상태에서 클래스가 선언된다는 것.

바로 중괄호{} 에 선언된 클래스의 생성자()를 new연산자로 호출 하겠다는 뜻이다.

그럼 생성자() 앞의 인터페이스는?? 

그 인터페이스의 구현 클래스라는 뜻이다. implement 인터페이스 와 같은 의미


실제 코드로 구현해보자


public class RemoteControlExample{

public static void main(String[]args){

RemoteControl rc = new RemoteControl(){

public void turnOn() {/실행문/}

public void turnOff() {/실행문/}

public void setVolume(int volume){/실행문/}

};

}  

}

위와 같은 방식이다.

이름이 없는 구현 클래스, 선언과 동시에 객체를 생성한다.

인터페이스의 추상메소드들을 모두 재정의 하는 실체메소드가 있어야 한다.

추가적으로 필드, 메소드를 선언할 수 있지만 익명 객체 안에서만 사용할 수 있고. 인터페이스 변수로 접근할 수 없다.   

주로 사용되는 곳은 

- UI프로그래밍 (Swing, Android)에서 이벤트 처리

-임시작업스레드를 만들기 위해

-자바8부터 지원하는 람다식의 경우 내부적으로 익명구현객체를 사용

사실 익명구현객체도 소스파일을 만들기는 한다. 하지만 기존의 클래스를 만들때와 같이 따로 만들어줄 필요는없고 익명구현객체가 있는 메인 클래스를 컴파일 하면 자바 컴파일러가 자동으로 소스파일을 만든다.

만약 익명구현객체가 있는 클래스의 이름이 [RemoteControlExample.class]라면 익명구현 객체의 파일명은

[RemoteControlExample$1.class]이런식으로 이름뒤에 $가 붙고 생성번호가 붙는다. 1번 부터 시작한다. 


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

객체는 다수의 인터페이스 타입으로 사용될수 있다. 개발코드 A에서도 B 에서도 사용가능하다. 

이런 느낌이다. 

인터페이스 A의 구현객체를 만들때 코드가 어떻게 되더라??

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

//인터페이스 A의 구현 실체메소드 선언

}

이런 모양이였다 그런데 인터페이스B는 어떻게 연결하는가???? 간단하다. 


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

//인터페이스 A의 구현 실체메소드 선언

//인터페이스 B의 구현 실체메소드 선언

}


이전에 배운 상속의 경우 다중상속이 불가능 하다고 했다 하지만 인터페이스의 경우 다중 선언이 가능하다. 

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

만약 하나라도 없으면 이 클래스는 추상 클래스로 선언해야한다. 예를 들어보자 

public interface RemoteControl {

int MAX_VOLUME = 10;

int MIN_VOLUME = 0 ;

void turnOn();

void turnOff();

void setVolume(int volume);

}


public interface Searchable {

void search(String url);

}


두개의  인터페이스를 선언한다. 물론 메소드는 추상메소드이다. 


public class SmartTelevision implements RemoteControl, Searchable{

private int volume;


@Override

public void search(String url) {

System.out.println(url + "을 검색합니다.");


@Override

public void turnOn() {

System.out.println("TV를 켭니다.");

}


@Override

public void turnOff() {

System.out.println("TV를 끕니다.");

}


@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("현재볼륨 : "+volume);

}

}


구현클래스이다 클래스명 뒤에 implements로 두개의 인터페이스를 선언하고
내부에는 두 인터페이스의 추상메소드를 모두 구현해준다.
하나라도 구현하지않으면 이 클래스는 abstract를 붙여 추상클래스로 만들어야 한다.

그리고 실행 클래스를  쓴다

public class RemoteControlExample {


public static void main(String[] args) {

SmartTelevision tv = new SmartTelevision();

RemoteControl rc = tv;

rc.turnOn();

rc.turnOff();

rc.setVolume(5);

Searchable searchable = tv;

searchable.search("www.naver.com");

}


}

구현 객체를 만들고 인터페이스타입에 대입한다. 두개의 인터페이스 모두 대입이 가능하고 호출또한 가능하다. 


'Java' 카테고리의 다른 글

인터페이스4  (0) 2019.01.19
인터페이스3  (0) 2019.01.18
인터페이스 (Interface)  (0) 2018.12.29
추상클래스(abstract)  (0) 2018.12.23
강제타입변환 Casting 및 몇가지  (0) 2018.12.22