지구정복

[JAVA] 인터페이스 본문

데이터 엔지니어링 정복/JAVA & JSP

[JAVA] 인터페이스

eeaarrtthh 2020. 11. 4. 23:10
728x90
반응형

 

ㅁ소개 

인터페이스 요소로는 상수 필드와 추상 메소드가 있다. 주로 추상 메소드를 많이 사용한다.

 

인터페이스에서 추상 메소드를 정의해놓으면

 

인터페이스를 구현하는 클래스(구현클래스)는 각 클래스 입맛에 맞게 인터페이스의 추상메소드를 재정의한다.

 

만약 구현클래스에서 인터페이스의 추상 메소드를 재정의하지 않으면 에러가 난다.

 

 

 

따라서 인터페이스는 인터페이스를 구현하는 구현 클래스의 설계를 제한하는 제약조건과 같다.

 

그럼 인터페이스를 왜 사용할까?

 

사용하는 이유는 인터페이스를 구현하는 구현 클래스로부터 생성된 객체들의 기능들을 코드의 수정없이 

간편하게 사용하기 위함이다. 이 말이 이해가 안될 수 있으므로 예제로 이해를 해보자.

 

 

만약 앞에 자판기가 있고 여기에는 '코코팜' '핫식스'가 있다고하자.

자판기에서는 다양한 음료수를 뽑을 수 있다. 이때 어떤 음료수를 뽑든지 간에 '뽑는다'라는 공통적인 기능이 있다.

 

'뽑는다'라는 공통적인 기능은 추상메소드이다.

이 추상메소드를 '뽑을 수 있음' 이라는 이름을 가지는 인터페이스로 정의한다. 

 

이를 코드화하면 아래와 같다.

interface 뽑을수있음 {
    void 뽑는다();    //추상메소드
}

 

이제 '뽑을수있음' 인터페이스를 구현하는 구현클래스를 정의하자.

 

'코코팜' 음료수는 인터페이스 '뽑을수있음'을 구현한 뒤 '뽑는다' 추상메소드를 재정의해야 한다.

코드화하면 아래와 같다. implements는 인터페이스를 구현한다는 의미이다.

class 코코팜 implements 뽑을수있음 {

    @Override    //추상메서드 재정의
    void 뽑는다() {
        System.out.println("코코팜을 뽑습니다.");
    }
    
}

 

코코팜말고 열심히 공부하기 위해서 '핫식스' 구현클래스도 만들어보자

class 핫식스 implements 뽑을수있음 {

    @Override    //추상메서드 재정의
    void 뽑는다() {
        System.out.println("핫식스를 뽑습니다.");
    }
    
}

 

이제 이 블로그 주인이 자판기에서 음료수를 뽑으려면 먼저 구현클래스로부터 객체를 만들고

 

이 객체를 인터페이스타입 참조변수에 저장한다.

 

그리고 참조변수에서 '뽑는다' 메소드를 사용하면 해당 객체의 재정의된 메소드가 실행될 것이다.

코드를 보면서 이해하자

main (String[] args) {
    뽑을수있음 a = new 코코팜();    //뽑을수있음 타입을 가지는 참조변수 a에 코코팜 객체의 주소를 저장
    뽑을수있음 b = new 핫식스();    //뽑을수있음 타입을 가지는 참조변수 b에 핫식스 객체의 주소를 저장
    
    a.뽑는다();        //'코코팜'의 재정의된 '뽑는다' 메소드 실행
    b.뽑는다();        //'핫식스'의 재정의된 '뽑는다' 메소드 실행
}

 

이러면 개발코드에서는 인터페이스의 '뽑는다'라는 메소드로 두 가지 기능을 할 수 있다.

 

나중에 만약 음료수의 종류가 2개가 아니라 100개가 되더라도 객체만 바꿔서 사용하면 '뽑는다'라는 메소드의 기능은 

사용할 수 있다. 

 

 

이제 책에있는 내용으로 알아보자.

 

 

 

ㅁ인터페이스 선언

인터페이스는 물리적 형태는 클래스와 동일하지만 선언하는 방법은 다르다.

 

interface 키워드를 사용한다.

 

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

 

인터페이스는 상수 필드추상 메소드만을 구성 멤버로 가진다.

인터페이스는 객체로 생성할 수 없기 때문에 생성자를 가질 수 없다.

 

interface 인터페이스이름 {
    //상수
    타입 상수이름 = 값;
    
    //추상 메소드
    타입 메소드이름 (매개변수, ...);
}

 

 

ㅇ상수 필드 선언

인터페이스는 객체 사용 방법을 정의한 것이므로 실행 시 데이터를 저장할 수 있는 인스턴스 또는 정적 필드를

선언할 수 없다. 그러나 상수 필드는 선언이 가능하다.

 

상수는 public static final로 선언한다. 인터페이스에서 이를 생략하더라도 자동적으로 붙게 된다.

package sec01.exam02;

public interface RemoteControl {
	public int MAX_VOLUME = 10;
	public int MIN_VOLUMN = 0;

}

 

 

ㅇ추상 메소드 선언

인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행된다. 그렇기 때문에 인터페이스의 메소드는 실행블록이

필요없는 추상 메소드로 선언한다.

 

추상 메소드는 리턴타입, 메소드이름, 매개변수만 기술되고 중괄호는 필요없다.

 

추상 메소드는 모두 public abstract의 특성을 갖기때문에 이를 생략해도 컴파일 과정에서 자동으로 붙는다.

package sec01.exam03;

public interface RemoteControl {
	//상수
	public int MAX_VOLUME = 10;
	public int MIN_VOLUMN = 0;
	
	//추상 메소드
	public void turnOn();
	public void turnOff();
	public void setVolume(int volume);
}

 

 

 

ㅁ인터페이스 구현

개발 코드가 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출한다.

 

객체는 인터페이스에서 정의된 추상메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 한다.

 

이러한 객체를 인터페이스의 구현객체(implement)라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 한다.

 

ㅇ구현 클래스

클래스 선언부에 implements 키워드를 추가하고 인터페이스 이름을 명시한다.

 

그리고 인터페이스에 선언된 추상 메소드의 실체 메소드를 선언해야 한다.

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

구현 클래스에서 인터페이스의 추상 메소드에 대한 실체 메소드를 작성할 때 주의할 점은

 

인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖기 때문에 public 보다 더 낮은 접근 제한으로

 

작성할 수 없다는 것이다.

 

 

 

예제로 인터페이스 RemoteControl의 구현 클래스 Television과 Audio를 설계하고 추상메소드를 재정의해보자.

package sec01.exam04;

public interface RemoteControl {
	//상수
	public int MAX_VOLUME = 10;
	public int MIN_VOLUMN = 0;
	
	//추상 메소드
	public void turnOn();
	public void turnOff();
	public void setVolume(int volume);
}
package sec01.exam04;

public class Television implements RemoteControl {
	//필드
	private int volume;
		
	@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_VOLUMN ) {
			this.volume = RemoteControl.MIN_VOLUMN;
		} else {
			this.volume = volume;
		}
		
		System.out.println("현재 TV 볼륨 : "+ this.volume);
	}
}
package sec01.exam04;

public class Audio implements RemoteControl {
	//필드
	private int volume;
		
	@Override
	public void turnOn() {
		System.out.println("Audio를 켭니다.");
	}

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

	@Override
	public void setVolume(int volume) {
		if ( volume > RemoteControl.MAX_VOLUME ) {
			this.volume = RemoteControl.MAX_VOLUME;
		} else if ( volume < RemoteControl.MIN_VOLUMN ) {
			this.volume = RemoteControl.MIN_VOLUMN;
		} else {
			this.volume = volume;
		}
		System.out.println("현재 Audio 볼륨 : "+ this.volume);
	}
}

 

 

구현 클래스가 작성되면 new 연산자로 구현 클래스의 객체를 생성할 수 있다. 

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

 

인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.

package sec01.exam04;

public class RemoteControlExample {

	public static void main(String[] args) {
		RemoteControl rc;
		
		rc = new Television();
		rc = new Audio();
	}
}

 

 

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

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

 

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

 

구현 클래스는 아래와 같이 작성되어야 한다.

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

 

위에 예제에 이어서 새로운 인터페이스를 정의한다.

package sec01.exam04;

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

 

이제 새롭게 다중 인터페이스를 구현하는 구현 클래스를 작성하자.

package sec01.exam04;

public class SmartTelevision implements RemoteControl, Searchable {
	//필드
	private int volume;
		
	@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_VOLUMN ) {
			this.volume = RemoteControl.MIN_VOLUMN;
		} else {
			this.volume = volume;
		}
		System.out.println("현재 TV 볼륨 : "+ this.volume);
	}
	
	@Override
	public void search(String url) {
		System.out.println(url+" 을 검색합니다.");
	}
}

 

구현 클래스로부터 구현 객체를 인터페이스 변수에 대입하자.

package sec01.exam04;

public class SmartTelevisionExample {

	public static void main(String[] args) {
		SmartTelevision tv = new SmartTelevision();
		
		RemoteControl rc = tv;
		Searchable searchable = tv;
	}
}

 

 

ㅁ인터페이스 사용

이제는 인터페이스로 구현 객체를 사용하는 방법을 알아보자.

 

클래스를 선언할 때 인터페이스는 (필드, 생성자) 또는 (메소드의 매개변수, 생성자) 또는 (메소드의 로컬 변수)

로 선언될 수 있다.

 

1번: 인터페이스가 필드 타입으로 사용될 경우, 필드에 구현 객체를 대입할 수 있다.

2번: 인터페이스가 생성자의 매개 변수 타입으로 사용될 경우, new 연산자로 객체를 생성할 때 구현 객체를

   생성자의 매개값으로 대입할 수 있다.

3번: 인터페이스가 로컬 변수 타입으로 사용될 경우, 변수에 구현 객체를 대입할 수 있다.

4번: 인터페이스가 메소드의 매개 변수 타입으로 사용될 경우, 메소드 호출 시 구현 객체를 매개값으로 대입할 수 있다.

 

아래 예제를 보면서 이해하자.

package sec01.exam04;

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번: 필드로 선언된 rc는 다음과 같이 사용가능하다.

MyClass myclass = new MyClass();
myclass.rc.turnOn();        //Television의 turnOn() 실행
myclass.rc.setVolume(5);    //Television의 setVolume(5) 실행

 

2번: 생성자의 매개 변수 타입으로 선언된 rc는 다음과 같이 사용가능하다.

MyClass( RemoteControl rc ) {
    this.rc = rc;
    rc.turnOn();
    rc.setvolume(5);
}

 

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

 

 

이제 실제로 사용해보자.

package sec01.exam04;

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 sec01.exam04;

public class MyClassExample {

	public static void main(String[] args) {
		System.out.println("1번)-------------------");
		
		MyClass my1 = new MyClass();
		my1.rc.turnOn();
		my1.rc.setVolume(5);
		
		System.out.println("2번)-------------------");
		
		MyClass my2 = new MyClass(new Audio());
		
		System.out.println("3번)-------------------");
		
		MyClass my3 = new MyClass();
		my3.methodA();
		
		System.out.println("4번)-------------------");
		
		MyClass my4 = new MyClass();
		my4.methodB(new Television());
	}
}

 

출력결과

1번)-------------------
TV를 켭니다.
현재 TV 볼륨 : 5
2번)-------------------
Audio를 켭니다.
현재 Audio 볼륨 : 5
3번)-------------------
Audio를 켭니다.
현재 Audio 볼륨 : 5
4번)-------------------
TV를 켭니다.
현재 TV 볼륨 : 5

 

 

출처

혼자공부하는자바 - 신용권

728x90
반응형
Comments