인터페이스
인터페이스란 실제 메소드를 정의하고 구현하는 것이 아니라 메소드의 헤더 부분(시그니처)만 선언하며,
이 메소드를 실제 정의하는 클래스에서 메소드 본체를 완성하는 것을 말한다.
개발 코드와 객체가 서로 통신하는 접점 역할을 하는데 개발 코드가 인터페이스의 메소드를 호출하면
인터페이스는 객체의 메소드를 호출시킨다. 여기서 개발 코드가 직접 객체의 메소드를 호출하면 되는데
왜 중간에 인터페이스를 두는 것인지 의문이 생길 수도 있다. 그 이유는 개발 코드를 수정하지 않고
사용하는 객체를 변경할 수 있도록 하기 위해 인터페이스를 사용하는 것이라고 할 수 있다.
즉, 코드 변경 없이 실행 내용과 리턴 값을 다양화할 수 있다는 장점을 가지게 된다는 것이다.
인터페이스는 상수 필드와 추상 메소드만을 구성 멤버로 가진다.
그리고 객체로 생성할 수 없기 때문에 생성자를 가질 수 없다.
public static final을 생략하더라도 컴파일 과정에서 자동으로 붙게 되고,
인터페이스에 선언된 추상 메소드 또한 모두 public abstract의 특성을 갖기 때문에
public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.
아래의 코드를 보고 설명을 이해해보자.
// 인터페이스
public interface Wearable {
void putOn(); // 기기 착용하기
void putOff(); // 기기 벗기
}
// 구현 클래스
public class Headphone implements Wearable{
private int volume;
Headphone(){
this.volume = 0;
}
// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
@Override
public void putOn() {
System.out.println("헤드폰을 착용합니다.");
}
// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
@Override
public void putOff() {
System.out.println("헤드폰을 벗습니다.");
}
public int getVolume() {
return volume;
}
public void setVolume(int volume) {
this.volume = volume;
System.out.println("볼륨을 " + volume +"로 변경했습니다.");
}
}
// 구현 클래스
public class Glasses implements Wearable{
// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
@Override
public void putOn() {
System.out.println("스마트 안경을 착용합니다.");
}
// 인터페이스에 선언된 추상 메소드의 실체 메소드 선언
@Override
public void putOff() {
System.out.println("스마트 안경을 벗습니다.");
}
public void look() {
System.out.println("눈에 보이는 모든 정보를 기록합니다.");
}
}
// Headphone을 상속한 클래스
public class SmartWatch extends Headphone{
public void bell() {
System.out.println("알림이 울립니다.");
}
}
public class WearableApp {
public static void main(String[] args) {
// 1번
Headphone headphone = new Headphone();
headphone.putOn();
headphone.setVolume(5);
headphone.putOff();
Glasses glasses = new Glasses();
glasses.putOn();
glasses.look();
glasses.putOff();
System.out.println();
// 2번
// Wearable 기기들을 관리 -> 같은 종류 데이터가 모일 방을 설정함
Wearable[] wearable = new Wearable[2];
// 실제 각방 크기는 그 방에 있는 데이터의 크기에 따름
// 업캐스팅
wearable[0] = new Headphone();
wearable[1] = new Glasses();
for(Wearable wrb : wearable) {
wrb.putOn();
wrb.putOff();
}
// 다운 캐스팅
((Headphone)wearable[0]).setVolume(5);
((Glasses)wearable[1]).look();
// 다운 캐스팅은 원래 자기 형식이어야 함
// ((Headphone)wearable[1]).look(); // 에러
System.out.println();
// 3번
SmartWatch sw = new SmartWatch();
Headphone hp = new SmartWatch();
Wearable wa = new SmartWatch();
sw.bell();
// 부모 클래스인 Headphone로 업캐스팅되어 Headphone의 메소드만 보임
// hp.ring(); // 에러
wa.putOn();
wa.putOff();
}
}
// 결과
// 1번
헤드폰을 착용합니다.
볼륨을 5로 변경했습니다.
헤드폰을 벗습니다.
스마트 안경을 착용합니다.
눈에 보이는 모든 정보를 기록합니다.
스마트 안경을 벗습니다.
// 2번
헤드폰을 착용합니다.
헤드폰을 벗습니다.
스마트 안경을 착용합니다.
스마트 안경을 벗습니다.
볼륨을 5로 변경했습니다.
눈에 보이는 모든 정보를 기록합니다.
// 3번
알림이 울립니다.
헤드폰을 착용합니다.
헤드폰을 벗습니다.
먼저 Wearable 인터페이스를 생성해주었다.
앞에서 말했다싶이 인터페이스에는 메소드를 실제 정의하는 클래스에서 실체 메소드를 작성해줘야한다.
그 실체 메소드를 정의해줄 클래스를 Headphone과 Glasses 클래스라고 하겠다.
그리고 그 실체 메소드를 정의한 클래스인 Headphone을 상속하는 클래스인
SmartWatch 클래스도 만들어주었다. 1번 결과의 코드는 당연한 결과이므로 따로 설명하지 않겠다.
바로 2번 결과의 코드를 보도록하자.
원래는 Headphone형이었던 상태에서 Wearable형으로 업캐스팅해준 것을 볼 수 있다.
업캐스팅은 abstract할 때 언급했다싶이 자식 객체를 부모 타입으로 변경하는 것임을 기억하고 넘어가자.
그렇다면 그 아래에 ()를 사용한 새로운 형식이 눈에 보일 것이다. 이는 다운 캐스팅이라고 하는데
다운 캐스팅은 업캐스팅했던 객체를 본래 자기 타입으로 다운시키는 것을 말한다.
즉, 업캐스팅했던 Wearable 객체를 본래 자기 타입인 Headphone과 Glasses로
다운시키는 것을 말하는데 각각 본래의 자기 자신으로 되돌아 갔으므로 Headphone 클래스의
setVolume() 메소드를 사용할 수 있게 되었고, Glasses 클래스도 look() 메소드를 사용할 수 있게 된 것이다.
그리고 그 아래줄은 당연하게도 원래 자기 형식으로 다운 캐스팅을 해야하는데 그렇지 않으므로
에러가 발생하게 되는 것이다. 3번도 주석한 부분만 짚고 넘어가면 충분하므로 이 부분만 언급하고 넘어가겠다.
이 부분이 에러가 발생한 이유는 hp 객체는 지금 Headphone 클래스로 업캐스팅이 되어있는 상태인데
SmartWatch 클래스의 bell() 메소드를 호출했기 때문이다.
즉, 이 말은 Headphone 클래스의 메소드만 사용할 수 있다는 뜻이다.
'국비 지원 > JAVA' 카테고리의 다른 글
[JAVA] 익명 함수와 람다 함수 (0) | 2023.05.21 |
---|---|
[JAVA] 다중 상속과 추상 클래스와 인터페이스의 차이점 (0) | 2023.05.20 |
[JAVA] abstract 추상 클래스와 추상 메소드 (+ 업캐스팅) (1) | 2023.05.15 |
[JAVA] 부모 생성자 호출 super() / 부모 메소드 호출 super (0) | 2023.05.15 |
MVC란 무엇일까? 또, DAO와 DTO, VO는 무엇일까? (0) | 2023.05.14 |