인터페이스란
▶하나의 틀, 규격을 말한다!
▶생성자가 없다. (인터페이스에서는 생성의 개념이 없다.)
▶추상클래스와 목적이 같은데, 즉 지정받거나 상속받는 클래스들의 공통요소를 묶기 위함이다.
하지만 추상클래스와는 차이점이 있다.
▶추상클래스와 인터페이스의 차이점
인터페이스 | 추상클래스 | |
동작 가능성 (생성자 유무) |
(단, 추상메서드일 경우)동작 실행 개념이 없기 때문에 실질적인 동작구현(블록 {})이 없다. (생성자 X) |
실행 개념이 있는 일반 메서드는 구현할 수 있지만 추상메서드의 경우, 동작구현을 하지 않는다.(블록 {}이 없다.) |
재정의 강제성 | 인터페이스에서 정의한 요소를 하위클래스가 모두 재정의하도록 강제성을 부여한다. | 재정의 강제성은 있지만 하위클래스가 필요한 요소를 선택해서 쓸수 있다. |
** 동작 가능성을 보면 차이점이 없는가 하지만, 생성자의 유무로도 구분할 수 있다. 인터페이스에서는 생성자가 없지만 추상클래스에선 생성자가 있을 수 있다.
▶공통점은
- 둘다 추상메서드를 갖고있다! (추상클래스는 일반메서드도 구현할 수 있다.)
- 재정의할 하위클래스가 있어야한다! 즉, 상속이 base로 깔려있다.
인터페이스의 재정의 강제성으로 인해, 여러 하위 클래스들 중 인터페이스의 모든 요소를 사용하고 싶지 않을 경우가 발생하는데, 그럴 때에는 차이점이 있는 추상클래스를 활용하게 되는데 인터페이스를 지정한 추상클래스를 상속받는 식으로 하여, 그 추상클래스에서 자신이 원하는 요소를 골라 재정의하면 된다. |
▶인터페이스안에는 상수 / 추상 메소드만 구성되었지만, 자바8이후로는 디폴트 메소드 / 정적 메소드 도 추가되었다.
▶상수 필드 선언
- 인터페이스는 상수 필드만 선언 가능.
- 인터페이스에 선언된 필드는 모두 public static final. (붙이지않아도 자동적으로 컴파일 과정에서 붙음)
- 선언과 동시에 초기값 지정.
- 객체 통해 접근 가능. (static 메서드만 인터페이스를 통해 접근해야함)
[public static final]int st_a = 10;
//예시
public interface A {
int st_a = 10;
}
class B implements A {}
public class Main {
public static void main(String[] args) {
A a = new B();
System.out.println(a.st_a);
}
}
▶추상 메서드 선언
- 인터페이스에선 기본적으로 실행 블록이 없는 추상 메서드로 선언
- 키워드 abstract를 붙이지 않아도 자동적으로 추상메서드로 여긴다.
- 인터페이스 통해 호출된 (추상)메서드는 최종적으로 객체에서 실행된다.
- 즉, 호출 방법만 기술할 뿐 블록 {} 코드가 없다.
- 인터페이스의 구현 클래스에서 실행코드를 작성하여 오버라이딩해야한다.
[public abstract] void abs_a (int b);
//예시
public interface A {
[abstract] void abs_a (int b);
}
class B implements A {
void abs_a (int b) {
System.out.println("인터페이스A의 추상메서드 abs_a의 구현메서드 : " + b);
}
}
▶디폴트 메서드 선언
- 자바 8에서 추가된 인터페이스의 새로운 멤버.
- 디폴트 메서드 == 인스턴스 메서드
- (기본 추상메서드와 달리) 실행 블록을 가지고 있는 메서드.
- default 키워드를 반드시 붙여야 한다.
- 기본적으로 public 접근 제한. (생략하더라고 컴파일과정에서 자동으로 붙음)
- 인터페이스를 지정한 구현클래스에서 디폴트 메서드를 오버라이딩할 수 있다. (추상메서드 구현과 같이.)
- 구현 클래스에서 오버라이딩해주지 않아도 된다. (필수 X)
[public]default String def_b (String st) {
...
}
//예시
public interface A {
default String def_b (String st) {
System.out.println("인터페이스A의 디폴트 메서드 def_b :" + st);
}
}
class B implements A {
String def_b (String st) {
System.out.println("인터페이스A를 구현하는 클래스B의 실체 메서드 def_b :" + st);
}
}
▶디폴트 메서드 사용
- 인터페이스만으로는 사용 불가. (인스턴스 객체로 호출 가능)
- 구현 객체가 인터페이스에 대입되어야 호출할 수 있는 인스턴스 메서드
- 모든 구현 객체가 가지고 있는 기본 메소드로 사용. (필요에 따라 구현 클래스가 디폴트 메서드 재정의해서 사용함)
▶디폴트 메서드 상속
- 디폴트 메서드가 있는 인터페이스를 상속한 자식 인터페이스에서 활용 방법
a. 디폴트 메서드를 단순히 상속만 받음.
b. 디폴트 메서드를 재정의해서 실행 내용을 변경.
c. 디폴트 메서드를 추상메서드로 재선언.
public interface Parent {
public void method1();
public default void method2() { ... }
}
/*단순히 상속만 받음*/
public interface Child1 extends Parent {
public void method3(); //새로운 추상메서드 선언.
}
/*재정의해서 실행 내용 변경*/
public interface Child2 extends Parent {
public void method2() { *** }
public void method3(); //새로운 추상메서드 선언.
}
/*추상 메서드로 재 선언*/
public interface Child3 extends Parent {
public void method2(); //추상메서드 재선언.
public void method3(); //새로운 추상메서드 선언.
}
메인코드
public class Main {
public static void main(String[] args) {
Child1 c1 = new Child1() { //익명 구현 객체, 클래스를 따로 선언하지 않고, 객체를 선언하면서 클래스안에서 구현할 메서드를 적어준다.
public void method1() { ... }
public void method3() { ... }
};
c1.method1(); //Child1 구현 객체의 method1() 호출
c1.method2(); //하위 인터페이스에서 재정의하지않았으므로 Parent의 method2()를 호출.
c1.method3(); //Child1 구현 객체의 method3() 호출
Child2 c2 = new Child2() { //익명 구현 객체
public void method1() { ... }
public void method3() { ... }
};
c2.method1(); //Child2 구현 객체의 method1() 호출
c2.method2(); //Child2 인터페이스에서 재정의했으므로 Child2 인터페이스의 method2() 호출
c2.method3(); //Child2 구현 객체의 method3() 호출
Child3 c3 = new Child3() { //익명 구현 객체
public void method1() { ... }
public void method2() { ... }
public void method3() { ... }
};
c3.method1(); //Child3 구현 객체의 method1() 호출
c3.method2(); //Child3 구현 객체의 method2() 호출
c3.method3(); //Child3 구현 객체의 method3() 호출
▶정적 메서드 선언
- 자바 8에서 추가된 인터페이스의 새로운 멤버
- static 영역에 있는 메서드.
- 인터페이스를 통해서만 접근할 수있다. 객체를 통해서 접근 X (아래처럼 main에서 인터페이스A. 를 통해 접근)
public interface A {
static int ex (int a) {
return a;
}
}
public static Main {
public static void main(String[] args) {
int b = A.ext (3);
}
}
▶[인터페이스 형식]
public interface InterA {
[static] int ST_A = 10; //상수
[public abstract] void abs_A(); //추상메서드
[public] default String def_A(String a) { //디폴트 메서드
...
}
[public] static void st_b (int b) { //정적 메서드
...
}
}
- 상수는 선언과 초기화가 동시에 이루어져야한다.
- 추상메서드는 코드구현 블록{} 이 없다.
- 디폴트&정적 메서드는 구현코드가 있다.
▶인터페이스를 이용한 다형성
▷다형성에서 자손 클래스의 인스턴스를 조상타입의 참조변수로 참조할 수 있다는것을 배웠다.
▷인터페이스도 인터페이스 타입의 참조변수로 인터페이스를 구현한 클래스의 인스턴스를 참조할 수 있고, 인터페이스 타입으로 형변환도 가능하다.
interface Inter {}
class A implements Inter {}
public class Main {
public static void main(String[] args) {
Inter a = new A;
}
}
}
다형성을 위해 간단히 적었다. 이런식으로 인터페이스 타입의 참조변수로 클래스 A의 인스턴스를 참조할 수 있다.
▷하나의 타입에 여러가지 객체를 대입해 다양한 실행 결과를 얻는 것 (자동타입 변환)
(인터페이스 RemoteControl에 여러가지 객체 (Tv와 Audio)를 대입해 turnOn()을 불러도 다양한 실행 결과를 얻을 수 있다.
→매개변수의 타입이 인터페이스인 경우, 어떠한 구현 객체도 매개값으로 사용 가능하다.
→또한 구현 객체에 메서드 실행 결과가 달라진다.
▷이런 다형성을 구현하는 기술로는
1. 위에서 쓰인 상속 또는 인터페이스의 자동 타입 변환
2. 오버라이딩
3. 객체를 부품화시킬 수 있어 유지보수 용이 (메서드의 매개변수로 사용)
Ex)
인터페이스A를 구현하는 B,C 클래스
B클래스의 자식 클래스 D
C클래스의 자식 클래스 E
B b = new B(); C c = new C(); D d = new D(); E e = new E(); |
-> | A a1 = b; A a2 = c; A a3 = d; A a4 = e; |
인터페이스 Tire의 예제를 보자.
한개의 인터페이스(의 메서드)로 여러 구현 클래스에 따라 다른 결과가 출력된다.
1. 인터페이스 Tire 코드
public interface Tire {
public void roll();
}
2. 인터페이스를 구현하는 클래스 HankookTire, KumhoTire 코드
//roll()을 구현해야함.
public class HankookTire implements Tire {
public void roll() {
System.out.println("한국타이어가 굴러갑니다.");
}
}
public class KumhoTire implements Tire {
public void roll() {
System.out.println("금호타이어가 굴러갑니다.");
}
}
3. 일반 클래스 Car, 메인메서드를 포함한 클래스 CarEx 코드
public class Car {
//인터페이스 Tire 타입의 변수 4개에 자식클래스들을 업캐스팅한 객체를 지정.
Tire frontLeftTire = (Tire)new HankookTire(); //업캐스팅할 때는 괄호 생략 가능
Tire frontRighttTire = new HankookTire();
Tire backLeftTire = new HankookTire();
Tire backRightTire = new HankookTire();
//일반 메서드
public void run {
//객체를 이용하여 roll()에 접근. HankookTire클래스의 메서드가 호출될 예정.
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
backRightTire.roll();
}
}
public class CarEx {
Car myCar = new Car(); //Car클래스의 객체 생성.
myCar.run(); //Car클래스의 메서드에 접근 가능. HankookTire()객체로 지정했으니 모두 한국타이어.
myCar.frontLeftTire = (Tire)new KumhoTire(); //괄호 생략 가능.
myCar.frontRightTire = new KumhoTire();
myCar.run(); //다시 KumhoTire()객체로 지정한 두개는 금호타이어로 출력됨.
}
[결과]
구분하기위해 구분선 '='을 넣어줌.
▶인터페이스 배열로 구현한 객체 관리
다른 클래스에서 만들어진 요소들이 같은 인터페이스 타입으로 지정돼있다면 같은 인터페이스 타입의 배열로 만들 수 있다.
같은 Tire 인터페이스라면,
Tire[] tire = { new HankookTire(), ...}
for (Tire tire : tires) {
...
}
위에서 다뤘던 Tire예제를 배열을 이용해서 더 편리하게 바꿔보겠다. 바뀔 코드는 Tire 타입을 4번이나 써야했던 Car 클래스이다. main메서드도 조금 바꿔줘야한다.
public class Car {
Tire[] tires = {
new HankookTire(),
new HankookTire(),
new HankookTire(),
new HankookTire()
};
public void run() {
for(Tire tire : tires) { //for each문을 이용했다.
tire.roll();
}
}
}
/*for each문 형식
for (타입 객체 : 반복될 대상 ) { //즉, 반복될 대상(배열 tires)를 객체 tire에 넣으면 그 객체는 실행문에서 쓰인다.
...//반복될 문장.
}
ex) for (String number : numbers) {
System.out.println(number);
}
*/
public class CarEx {
public static void main(String[] args) {
Car myCar = new Car();
myCar.run();
myCar.tires[0] = new KumhoTire();
myCar.tires[1] = new KumhoTire();
myCar.run();
}
}
결과는 같다.
▶인터페이스끼리의 상속
- 같은 인터페이스끼리의 상속이기 때문에 implements가 아닌 extends
- 인터페이스에선 다중 상속이 가능하기 떄문에,
public interface 하위인터페이스 extends 상위인터페이스1, 2 {...}
Ex) 인터페이스A { 메서드 A } 인터페이스B extends A { 메서드 B } 인터페이스C extends B { 메서드 C } 클래스C implements 인터페이스C { 메서드 A,B,C 구현 } |
클래스C가 인터페이스C를 지정받는다는 것은 인터페이스A,B도 지정받는 것이기 때문에 메서드 A,B도 재정의해야 한다.
인터페이스 구현에 대해서 알아봤으니, 직접 예제로 인터페이스 RemoteControl 를 구현해보겠다!
인터페이스에는 상수2개와 추상메서드 3개, 디폴트 메서드 1개, 정적 메서드 1개가 있다.
public interface RemoteControl {
//상수
int MAX_VOLUME = 10;
int MIN_VOLUME = 0;
//추상메서드
void turnOn();
void turnOff();
void setVolume(int volume);
//디폴트 메서드
default void setMute(boolean mute) {
if (mute)
System.out.println("무음 처리합니다.");
else
System.out.println("무음 해제합니다.");
}
//정적 메서드
static void changeBattery() {
System.out.println("건전지를 교체합니다.");
}
}
▶구현 객체와 구현 클래스
- 구현 객체; 인터페이스의 추상 메서드에 대한 실체 메서드를 가진 객체
- 구현 클래스 ; 구현 객체를 생성하는 클래스
- 구현 클래스에는 모든 추상메서드의 실체 메서드를 작성해야 한다. (메서드의 선언부가 정확히 일치해야한다.)
public class 구현클래스명 implements 인터페이스명 {
... //인터페이스에 선언된 추상 메서드의 실제 메서드 블록 구현
}
이번에는 인터페이스를 지정한 클래스 2개(Audio, Tv)와 메인 메서드가 들어간 RemoteControlEx 클래스를 적어보겠다.
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) {
//볼륨 제한이 0~10인데, 최댓값보다 크게하면 최댓값으로 최솟값보다 작게하면 최댓값으로, 아니면 그 볼륨으로 설정.
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 볼륨:"+volume);
}
}
public class Tv 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 볼륨: "+ volume);
}
}
public class RemoteControlEx {
public static void main(String[] args) {
System.out.println(RemoteControl.MAX_VOLUME); //인터페이스로 접근 가능한 상수
System.out.println(RemoteControl.MIN_VOLUME);
RemoteControl.changeBattery(); //(o),static 메서드는 인터페이스를 통해서만 접근 가능.(단, 재정의한 클래스의 객체에선 접근할수는 있음.)
// RemoteControl.setMute(false); (x),디폴트(인스턴스) 메서드는 인터페이스를 제외한 모든 객체가 가능.
Audio audio = new Audio();
audio.turnOn();
audio.setVolume(20);
audio.setMute(false);
audio.changeBattery(); //(o),Audio 클래스에서 재정의한 메서드는 접근 가능. but 밑줄생김..
//그래서, audio를 Audio로 바꿨더니 밑줄 사라짐. static 메서드이기 때문에 객체보단 클래스로 접근하는 것이 맞는 것 같음.
audio.turnOff();
RemoteControl r1; //레퍼런스 변수
r1 = new Audio();
r1.setVolume(10);
System.out.println(r1.MAX_VOLUME);
//static 메서드와 달리 상수에는 객체로 접근 가능하지만 밑줄 생김..
r1.setMute(false); //디폴트 메서드는 하위클래스에서 재정의하지 않아도 객체로 접근 가능.
// r1.changeBattery(); (x),인터페이스를 통해서만 static메서드에 접근 가능.
// r1.a = 3; (x),자식이 갖고있는 건 부모가 접근 못한다.
RemoteControl r2;
r2 = new Tv();
r2.turnOn();
r2.setVolume(8);
r2.setMute(true);
r2.turnOff();
// r2.changeBattery(); //Tv()에선 changeBattery()를 재정의조차 안했으므로 아예 오류 발생.
/*default setMute()와 static changeBattery()는 재정의 강제성이 있지 않음.*/
}
}
[결과]
* 헷갈림을 방지하기위해 다른 객체가 사용되기 전에 구분선을 그었다.
기억하고 넘어가야 할 것은
▶인터페이스에서 정의&구현된 디폴트메서드와 static 메서드는 하위클래스에서 재정의해주지 않아도 된다.
▶디폴트메서드(setMute())는 인터페이스를 제외한 모든 객체가 접근 가능하다.
(즉, RemoteControl.setMute(false);는 불가능하다!)
▶static 메서드(changeBattery())는 재정의한 클래스타입의 클래스 객체만 접근 가능하고 다른 경우(재정의했지만 인터페이스타입의 클래스 객체 or 재정의도 안한 클래스 객체)는 접근 불가능.
디폴트메서드와 static메서드 중심의 예제를 한개 더 보겠다.
1. 인터페이스 Ex1 코드
public interface Ex1 {
int a = 0; //인터페이스에서 상수 키워드를 붙여주지않아도 컴파일과정에서 자동으로 붙는다.
//상수는 선언과 동시에 기본값이 초기화되어야한다.
void in_a(int a); //추상메서드. 컴파일과정에서 자동으로 abstract 붙는다.
default void de_a(int a) {
System.out.println("디폴트 메소드 동작," + a);
}
static void st_a(int a) {
System.out.println("스테틱 메소드 동작," + a);
}
}
2. 실행 코드인 T1 클래스
public class T1 {
public static void main(String[] args) {
Ex1.st_a(4); //static 메서드는 인터페이스로 접근해야한다.
// Ex1.df_a(10); (x), 디폴트(인스턴스)메서드는 인터페이스가 아닌, 인스턴스 객체로 접근해야한다.
어려운 내용이니 다음 글에서 기본에 충실한 예제 한개를 다뤄보겠다.
익명 구현 객체 (난이도 ★★★★☆)
▶익명 구현 객체란, 명시적으로 클래스를 구현하지 않고, 바로 객체를 선언하면서 클래스를 선언하는 방식.
▶이름그대로 해석해보면, 익명구현 객체란 객체를 한번 찍어내기위한 익명의(이름없는) 클래스.
이름이 존재한다는 것은 다음에 이름을 통해 재호출/재사용 할 수 있다는 의미.
그렇기 때문에 익명 구현 객체는 일회적으로, 딱 한번 쓰이는 것.
▶익명 구현 객체는 보통 인터페이스를 구현하는 클래스를 적을 때 쓰인다.
▶즉, 이름없는 구현 클래스 선언과 동시에 구현 객체 생성.
▶이 구현{} 안에는 인터페이스의 추상 메서드들을 모두 재정의하는 실체 메서드가 있어야 한다!
▶이 블록 안에서 추가적인 필드와 메서드 선언이 가능하지만, 익명 객체 안에서만 사용할 수 있다!
(또한 인터페이스 변수로 접근이 불가능하다.)
▶간단히 말하면, 인터페이스는 생성자가 없어. 그 생성자를 채워주고 추상이 없게 재정의하는것. "익명 구현 객체"
▶[형식]
인터페이스명 변수명 = new 인터페이스명(
실체화할 메서드() {} //인터페이스에서 선언된 추상메서드의 실체 메서드로 재정의.
실체화할 메서드2() {}
);
▶이전에 썼던 예제 RemoteControl 인터페이스로 익명 구현 객체를 만들어본다면
RemoteControl field = new RemoteControl(
public void turnOn() {...}
public void turnOff() {...}
public void setVolume(int volume) {...}
//디폴트 메서드 setMute()와 정적 메서드 changeBattery()를 제외한 추상메서드는 강제성있음!
);
원래는
1. RemoteControl을 구현하는 클래스(Audio, Tv)를 선언하고,
2. main 메서드에서 RemoteControl field = new RemoteControl(); 과 같이 객체를 생성하지만
1. 클래스를 따로 선언하지 않고,
2. main 메서드에서 바로 객체를 생성하면서 생성자 괄호 안에 클래스의 내용을 구현하는 것이다!
즉, 구현 객체를 선언하는 것이다.
-> RemoteControl 인터페이스는 생성자가 없고 추상메서드가 있으니까, 생성자의 속을 채워주는데,
추상메서드의 실체메서드들로 채우는 것이라고 생각하면 된다!
만약 RemoteControl 인터페이스를 전에 보여준 코드와 똑같이 선언하고,
그 인터페이스를 구현하는 Ex 클래스의 코드가 위의 Audio 클래스 코드와 같다고 치자.
main에서 익명구현객체를 선언하는데, 내용 없는 재정의를 한다면, 결과는 어떻게 될까?
이유는, 메서드에 접근하는 객체 rc가 구현 객체인데, 동시에 재정의한 클래스의 내용에 아무것도 없기 때문에 출력결과가 없는 것이다.
여기에 만약 Ex와 관련된 인스턴스로 접근하기만 하면 Ex 클래스에서 재정의한 내용으로 출력될 것이다.
즉, 익명 구현 객체는 인터페이스타입의 객체와 그 객체가 접근할 재정의된 메서드를 동시에 선언한 셈이다.
▷익명 구현 객체가 쓰이는 경우
(1) 필드 초기값으로 쓰임.
(위에서 설명한 예시가 필드 초기값으로 쓰이는 경우이다. 즉, 객체(필드)를 선언할때 익명 구현 객체가 쓰인다.)
(2) 입력 매개변수로 쓰임.
(3) 매개변수의 대입으로 쓰임.
다중 인터페이스 구현 클래스
인터페이스에서는 다중 상속이 가능하기 때문에, 구현 클래스가 여러개의 인터페이스를 지정받을 경우, 인터페이스에서 정의된 추상메서드들을 재정의해주면 된다.
public class 구현클래스명 implements 인터페이스A, 인터페이스B {
... //인터페이스 A,B에 선언된 추상메서드의 실체 메서드 선언
}
▶인터페이스 Searchable, RemoteControl을 지정받는 Tv클래스와 Ex 메인
RemoteControl 인터페이스는 위에서 적은 코드와 똑같기때문에 생략하고,
1. Searchable 인터페이스와 구현클래스 Tv
public interface Searchable {
void search(String url);
}
public class Tv implements RemoteControl, Searchable {
private int volume; //필드
//RemoteControl인터페이스의 추상메서드들의 실체메서드 구현
public void turnOn() { System.out.println("Tv를 켭니다."); }
public void turnOff() { System.out.println("Tv를 끕니다."); }
public void setVolume(int volume) {
if(volume>MAX_VOLUME) { this.volume = RemoteControl.MAX_VOLUME; }
else if (volume < MIN_VOLUME) { this.volume = RemoteControl.MIN_VOLUME; }
else { this.volume = volume; }
System.out.println("현재 Tv 볼륨 : "+ volume);
}
//Searchable 인터페이스의 추상메서드의 실체메서드 구현
public void search(String url) {
System.out.println(url+"을 검색합니다.");
}
}
2. main메서드를 포함한 클래스 Ex
public class Ex {
public static void main(String[] args) {
Tv tv = new Tv();
RemoteControl rc = tv;
Searchable sb = tv;
// rc.search("K"); (x),rc입장에선 search 메서드가 없음.
sb.search("K");
rc.turnOn();
// sb.turnOn(); (x),sb입장에선 turnOn,turnOff,setVolume 메서드가 없음. 접근 불가능.
}
}
/*업캐스팅이 두가지로 쓰일 수 있다. 왜? 지정한 인터페이스가 두개니까.
tv 객체는 두개를 동시에 다른 타입의 변수에 저장했는데,
rc는 RemoteControl만,
sb는 Searchable만 쳐다보는 것이라고 생각하면 쉽다. */
[결과]
'언어 > Java' 카테고리의 다른 글
기초 #10. 추상 클래스 Shape (추상클래스 예제편) (0) | 2020.09.01 |
---|---|
기초 #9. 인터페이스 Soldier (인터페이스 예제편) (0) | 2020.09.01 |
기초 #7. 중첩 클래스(인스턴스 멤버 클래스, 정적 멤버 클래스, 로컬 클래스) (0) | 2020.08.30 |
기초 #6. 인터페이스 예습 (0) | 2020.08.23 |
기초 #5. 추상클래스 (ex. 계산기 프로그램) (0) | 2020.08.22 |