8.1인터페이스의 역할
- 자바에서 인터페이스는 객체의 사용 방법을 정의한 타입이다.
- 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할을 한다.
- 인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역할을 한다.
개발 코드가 직접 객체의 메소드를 호출하면 간단한데, 중간에 인터페이스를 두는 이유?
개발 코드를 수정하지 않고, 사용하는 객체를 변경할 수 있도록 하기 위함이다.
인터페이스는 하나의 객체가 아니라여러 객체들과 사용이 가능하므로,
어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있다.
8.2 인터페이스의 선언
8.2.1인터페이스의 선언
[public] interface 인터페이스명{}
public interface RemoteControl{}
interface 인터페이스명{
//상수
타입 상수명 = 값;
//추상 메서드
타입 메소드명(매개변수);
//디폴트 메소드
default 타입 메소드명(매개변수){}
//정적 메소드
static 타입 메소드명(매개변수){}
}
- 상수 필드
인터페이스는 객체 사용 설명서이므로 런타입 시 데이터를 저장할 수 있는 필드를 선언할 수 없다.
그러나 필드 선언은 가능하다. 상수는 인터페이스에 고정된 값으로 런타임 시에 데이터를 바꿀 수 없다. - 추상 메소드
추상 메소드는 객체가 가지고 있는 메소드를 설명한 것으로 호출할 때 어떤 매개값이 필요하고, 리턴 타입이 무엇인지만 알려준다.
실제 실행부는 객체(구현 객체)가 가지고 있다. - 디폴트 메소드
디폴트 메소드는 인터페이스에 선언되지만 사실은 객체가 가지고 있는 인스턴스 메소드라고 생각하자 - 정적 메소드
디폴트 메소드와는 달리 객체가 없어도 인터페이스만으로 호출이 가능하다.
8.2.2 상수 필드 선언
- 인터페이스는 데이터를 저장할 수 없기 때문에 데이터를 저장할 인스턴스 또는 정적 필드를 선언 할 수 없지만
상수 필드만 선언할 수 있다.
public interface RemoteControl{
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
}
8.2.3 추상 메소드 선언
- 추상 메소드는 리턴 타입, 메소드명, 매개 변수만 기술되고 중괄호 {}를 붙이지 않는 메소드를 말한다.
- 인터페이스에 선언된 추상메소드는 모두 public abstract의 특성을 가지기 때문에 생략하더라도 자동적으로 컴파일과정에서 붙게 된다.
public interface RemoteControl{
public int MAX_VOLUME = 10;
public int MINX_VOLUME = 0;
//추상 메소드
public void turnOn();
public void turnOff();
public void setVolume(int volume);
}
8.2.4디폴트 메소드 선언
- 형태는 클래스의 인스턴스 메소드와 동일한데, default 키워드가 리턴 타입 앞에 붙는다.
[public] default 리턴타입 메소드명(매개변수){...}
interface RemoteControl{
default void setMute(boolean mute){
//실행 내용까지 작성
if(mute){
...
}else{
...
}
}
}
8.2.5 정적 메소드 선언
- 클래스의 정적 메소드와 완전 동일하다.
- public 특성을 갖기 때문에 public을 생략하더라도 자동적으로 컴파일 과정에서 붙게된다.
[public] static 리턴타입 메소드명(매개변수,...){}
interface RemoteControl{
...
static void changeBattery(){
...
}
}
8.3인터페이스의 구현
- 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출한다.
- 객체는 인터페이스에서 정의된 추상 메소드와 동일한 메소드 이름, 매개 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 한다.
- 이러한 객체를 인터페이스의 구현(implement) 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 한다.
8.3.1구현 클래스
- 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 클래스 선언부에 implements 키워드를 추가하고 인터페이스명을 명시해야한다.
- 인터페이스에 선언된 추상 메소드의 실체 메소드를 선언해야 한다.
public class 구현클래스명 implments 인터페이스명{
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖기 때문에 public보다 더 낮은 접근 제한으로 작성할 수 없다.
public abstract class Television implements RemoteControl{
public void trunOn(){}
public void turnOff(){}
//setVolume메소드 아직 구현 안함
}
- 인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 한다.
인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.
8.3.2익명 구현 객체
- 일회성의 구현 객체를 만들기 위해 소스 파일을 만들고 클래스를 선언하는 것은 비효율적이다.
인터페이스 변수 = new 인터페이스(){
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}
- 인터페이스(){}는 인터페이스를 구현해서 중괄호{}와 같이 클래스를 선언하라는 뜻이고,
new 연산자는 이렇게 선언된 클래스를 객체로 생성한다. - 중괄호{}에는 인터페이스에 선언된 모든 추상 메소드들의 실체 메소드를 작성해야 한다.
RemoteControl rc = new RemoteControl(){
public void turnOn(){//내부 구현};
public void turnOff(){//내부 구현};
public void setVolume(int volume){//내부 구현}
}
8.3.3 다중 인터페이스 구현 클래스
- 인터페이스 A와 인터페이스 B가 객체의 메소드를 호출할 수 있으려면 객체는 이 두 인터페이스를 모두 구현해야 한다.
public class 구현클래스명 implements 인터페이스A, 인터페이스B{}
- 다중 인터페이스를 구현할 경우, 구현 클래스는 모든 인터페이스의 추상 메소드에 대해 실체 메소드를 작성해야 한다.
- 하나라도 없으면 추상 클래스로 선언해야 한다.
public class SmartTelevision implements RemoteControl, Searchable{
//remoteControl구현
public void turnOn(){//구현}
public void turnOff(){//구현}
public void setVolume(){//구현}
// searchable구현
public void search(String url){
//구현
}
}
8.4 인터페이스 사용
- 인터페이스로 구현 객체를 사용하려면 다음과 같이 인터페이스 변수를 선언하고 구현 객체를 대입해야 한다.
- 인터페이스 변수는 참조 타입이기 때문에 구현 객체가 대입될 경우 구현 객체의 번지를 저장한다.
RemoteControl rc;
rc = new Television();
rc = new Audio();
- 개발 코드에서 인터페이스는 클래스의 필드, 생성자, 또는 메소드의 매개 변수, 생성자 또는 메소드의 로컬 변수로 선언될 수 있다.
public class MyClass{
RemoteControl rc = new Television();
MyClass(RemoteControl rc){
this.rc = rc;
}
void methodA(){
RemoteControl rc = new Audio();
}
void methodB(RemoteControl rc){}
// mc.method(new Audio());
}
8.4.1 추상 메소드 사용
RemoteControl rc = new Televesion();
rc.turnOn(); -> Television의 turnOn
rc.turnOff(); -> Television의 turnOff
RemoteControl rc = new Audio();
rc.turnOn(); -> Audio의 turnOn
rc.turnOff(); -> Audio의 turnOff
8.4.2 디폴트 메소드 사용
- 인터페이스에 선언되지만, 인터페이스에서 바로 사용 할 수 없다.
- 디폴트 메소드는 추상 메소드가 아닌 인스턴스 메소드이므로 구현 객체가 있어야 사용할 수 있다.
RemoteControl.setMute(true) //안됨
RemoteControl rc = new Television();
rc.setMute(true);//가능
- 디폴트 메소드는 인터페이스의 모든 구현객체가 가지고 있는 기본 메소드라고 생각하면된다.
- 디폴트 메소드의 내용이 맞지 않아 수정이 필요하면 재정의 해서 사용할 수 있다.(override)
8.4.3 정적 메소드 사용
- 인터페이스의 정적 메소드는 인터페이스로 바로 호출이 가능하다.
RemoteControl.changeBattery();
8.5 타입 변환과 다형성
- 인터페이스도 다형성을 구현하는 기술이 사용된다.
- 다형성*: 하나의 타입에 대입되는 객체에 따라서 실행 결과가 다양한 형태로 나오는 성질
- 상속은 같은 종류의 하위 클래스를 만드는 기술, 인터페이스는 사용 방법이 동일한 클래스를 만드는 기술이다.
둘 다 다형성을 구현하는 기술이다. - 프로그램 소스 코드는 변함이 없는데, 구현 객체를 교체함으로써 프로그램의 실행 결과가 다양해진다.
- A클래스에서 B클래스로 바꿀때, 메소드와 이름, 매개변수가 다르다면 직접 찾아가 해당 부분을 수정해야 한다.
하지만 동일하다면 메소드 호출 코드는 수정할 필요 없이 객체 생성 부분만 변경하면 된다.
//I i = new A();
I i = new B();
i.method1();
i.method2();
- 인터페이스타입으로 매개 변수를 선언하면 메소드 호출 시 매개값으로 여러 가지 종류의 구현 객체를 줄 수 있기 대문에 메소드 실행 결과가 다양하게 나온다.
public void useRemoteControl(RemoeteControl rc){}
useRemoteControl(new Televesion())
useRemoteControl(new Audio())
8.5.1자동 타입 변환
구현 객체가 인터페이스 타입으로 변환되는 것은 자동 타입 변환에 해당한다.
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;
8.5.2 필드의 다형성
- 자동차를 설계할 때 같은 필드 타입으로 타이어 인터페이스를 선언하게 되면
필드 값으로 한국 타이어 또는 금호 타이어 객체를 대입할 수 있다.
public class Car{
//가능
Tire frontLeftTire = new HankookTire();
Tire frontRightTire = new HankookTire();
Tire backLeftTire = new HankookTire();
Tire backRightTire = new HankookTire();
}
//가능
Car myCar = new Car();
myCar.frontLeftTire = new KumhoTire();
- 타이어를 교체하기 전에는 한국타이어의 메소드가 실행되지만, 교체후에는 금호타이어의 메소드가 실행된다.
8.5.4 매개 변수의 다형성
public class Driver{
public void drive(Vehicle vehicle){
vehicle.run();
}
}
public interface Vehicle{
public void run();
}
public class Bus implements Vehicle{
//run구현
}
Driver driver = new Driver();
Bus bus = new Bus();
driver.drive(bus);
8.5.5 강제 타입 변환
- 구현 객체가 인터페이스 타입으로 자동 변환되면, 인터페이스에 선언된 메소드만 사용 가능하다는 제약 사항이 따른다.
경우에 따라서 구현 클래스에 선언된 필드와 메소드를 사용해야 할때 강제 타입변환을 해서 다시 구현 클래스 타입으로
변환한 다음, 구현 클래스의 필드와 메소드를 사용할 수 있다.
구현클래스 변수 = (구현클래스) 인터페이스변수;
interface Vehicle{
void run();
}
class Bus Implements Vehicle{
void run(){}
void checkFare(){}
}
Vehicle vehicle = new Bus();
vehicle.run(); //가능
vehicle.checkFare() //불가능
Bus bus = (Bus) vehicle;// 강제 타입 변환
bus.checkFare() //가능
8.5.6 객체 타입 확인
- 강제 타입 변환은 구현 객체가 인터페이스 타입으로 변환되어 있는 상태에서 가능하다.
- 어떤 구현 객체가 변환되어 있는지 알 수 없는 상태에서 무작정 변환을 할 경우 ClassCastException이 발생할 수 있으니
instanceof로 확인하고 변환하자
if(vehicle instanceof Bus){
Bus bus = (Bus) vehicle;
}
8.6 인터페이스 상속
- 다중 상속을 허용한다.
public interface 하위인터페이스 exnteds 상위인터페이스1, 상위인터페이스2{}
아래와 같이 타입 변환도 가능하다.
하위인터페이스 변수 = new 구현클래스();
상위인터페이스1 변수 = new 구현클래스();
상위인터페이스2 변수 = new 구현클래스();
하위 인터페이스 타입으로 변환이 되면 상, 하위 인터페이스에 선언된 모든 메소드를 사용할 수 있다.
상위 인터페이스 타입으로 변환되면 상위 인터페이스에 선언된 메소드만 사용가능하다.
public interface InterfaceA{
public void methodA();
}
public interface InterfaceB{
public void methodB();
}
public interface InterfaceC extends InterfaceA, InterfaceB{
public void methodC();
}
public class ImplementationC implements InterfaceC{
//methodA,B,C구현
}
ImplementationC impl = new ImplementationC();
InterfaceA ia = impl;
ia.methodA();
InterfaceB ib = impl;
ib.methodB();
InterfaceC ic = impl;
ic.methodA();
ic.methodB();
ic.methodC();
8.7 디폴트 메소드와 인터페이스확장
디폴트 메소드는 인터페이스에 선언된 인스턴스 메소드이기 때문에 구현 객체가 있어야 사용 할 수 있다.
선언은 인터페이스에서 하고 사용은 구현 객체를 통해 해야한다.
8.7.1 디폴트 메소드의 필요성
- 인터페이스에서 디폴트 메소드를 허용한 이유는 기존 인터페이스를 확장해서 새로운 기능을 추가하기 위함이다.
- 이전에 개발한 구현 클래스를 그대로 사용할 수 있으면서 새롭게 개발하는 클래스는 디폴트 메소드를 활용 할 수 있다.
public interface MyInterface{
public void method1();
}
public class MyClassA implements MyInterface{
@Override
public void method1(){
//구현
}
}
MyInterface 수정 발생
public interface MyInterface{
public void method1();
public default void method2(){
//구현
}
}
위와 같이 변동이 생겨도 기존 MyClassA는 컴파일 오류가 발생하지 않는다.
그러면서 동시에 method2기능이 필요한 MyClassB는 필요에 따라 해당 기능을 재정의 해서 사용할 수 있다.
public class MyClassB implements MyInterface{
public void method1(){
//구현
}
@Override
public void method2(){
//재정의
}
}
8.7.2 디폴트 메소드가 있는 인터페이스 상속
자식 인터페이스에서 디폴트 메소드를 활용하는 방법3가지
- 디폴트 메소드를 단순히 상속
- 디폴트 메소드를 재정의해서 실행 내용 변경
- 디폴트 메소드를 추상 메소드로 재선언한다
public interface ChildInterface3 extends ParentInterface{
@Override
public void method2(); //추상 메소드로 재선언
public void method3();
}
이경우 ChildInterface3 인터페이스를 구현 하는 클래스는 method1,method2,method3의 실체 메소드를 모두 가지고 있어야 한다.
'책 > 이것이 자바다' 카테고리의 다른 글
[11장]기본 API클래스 (0) | 2022.02.14 |
---|---|
[10장]예외처리 (0) | 2022.02.14 |
[9장]중첩 클래스와 중첩 인터페이스 (0) | 2022.02.14 |
[7장] 상속 (0) | 2022.02.14 |
[6장] 객체 (0) | 2022.02.14 |