인터페이스의 다형성
- 다형성을 구현하기 위해 필요한 2가지 : 메소드 재정의와 타입 변환
- 상속과 마찬가지로 인터페이스에서도 이 두 가지 기능이 제공됨
- 상속과 차이점 : 상속은 같은 종류의 하위 클래스 생성, 인터페이스는 사용 방법이 동일한 클래스를 만드는 기술
- 인터페이스의 다형성이 필요한 이유 : 소스 코드의 변함 없이 구현 객체를 빠르고 쉽게 교체하기 위해서이다
- 이를 통해 구현 객체를 쉽게 교체하여 실행 결과가 다양해져 인터페이스의 다형성을 이루게 됨
- 구현 객체 교체 예시
interface I { // 인터페이스 생성
void method1();
void method2();
}
// I i = new A(); // 이 코드를 아래 코드로 수정함
I i = new B();
i.method1(); // 이 부분을 수정하지 않아도 됨
i.method2();
자동 타입 변환
[인터페이스] [변수명] = 구현객체; // 자동 타입 변환
- 추가로, 인터페이스 구현 클래스를 상속한 자식 클래스가 만들어졌다면 그 자식 객체 역시 인터페이스 타입으로 자동 타입 변환 가능하다.
- 자동 타입 변환을 이용하여 필드나 매개변수의 다형성을 구현할 수 있다.
- 필드와 매개변수의 타입으로 인터페이스를 사용하면 다양한 구현 객체를 대입하여 실행 결과를 다양하게 할 수 있음
필드의 다형성 예제
- 구성 :
인터페이스 : Tire
구현 클래스 : Tire HankookTire와 KumhoTire
필드 선언 클래스 : Car
실행 코드 : CarExample
<인터페이스>
interface Tire {
// 추상 메소드
public void roll(); // roll() 메소드 호출 방법 설명
}
<구현 클래스>
class HanKookTire implements Tire {
@Override
public void roll() {
System.out.println("한국 타이어가 굴러갑니다.");
}
}
class KumhoTire implements Tire {
@Override
public void roll() {
System.out.println("금호 타이어가 굴러갑니다.");
}
}
<필드의 다형성>
class Car {
// 필드 타입으로 Tire 인터페이스 사용
// 필드 값으로 한국 타이어나 금호 타이어 객체 대입 가능
// 자동 타입 변환이 일어남
Tire frontLeftTire = new HanKookTire();
Tire frontRightTire = new HanKookTire();
Tire backLeftTire = new HanKookTire();
Tire backRightTire = new HanKookTire();
// Car 객체의 run() 메소드에서 Tire 인터페이스에서 선언된 roll() 메소드 호출
void run() {
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
backRightTire.roll();
}
}
<실행 코드>
public class CarExample {
public static void main(String[] args) {
// Car 객체 생성
Car myCar = new Car();
myCar.run(); // 교체 전이라 모두 한국타이어가 뜸
System.out.println();
// 초기값으로 대입한 구현 객체 대신 다른 구현 객체를 대입할 수도 있음
// 이것이 바로 타이어 교체
myCar.frontLeftTire = new KumhoTire();
myCar.frontRightTire = new KumhoTire();
myCar.run(); // 앞 바퀴 교체 후 금호타이어로 바뀜
System.out.println();
// run() 메소드를 수정하지 않아도 다양한 roll() 메소드의 실행 결과를 얻게 됨
// 이것이 필드의 다형성
}
}
매개 변수의 다형성
- 메소드의 매개변수 타입으로 인터페이스를 사용할 수 있는데 그 경우에는 구현 객체를 매개값으로 사용할 수 있다.
- 그러면 어떤 구현 객체를 매개값으로 사용하냐에 따라 메소드의 실행 결과는 다양해진다.
- 이것이 바로 인터페이스의 매개 변수의 다형성이다.
매개 변수의 다형성 예제
<인터페이스>
interface Vehicle {
public void run(); // 추상 메소드
}
<매개변수로 인터페이스 사용>
class Driver {
public void drive(Vehicle vehicle) {
// main 메소드에서 vehicle 자리에 Bus 객체와 Taxi 객체를 보내줄 예정
vehicle.run();
}
}
<구현 클래스>
// 구현 클래스
class Bus implements Vehicle {
@Override
public void run() {
System.out.println("버스가 달립니다.");
}
}
//구현 클래스
class Taxi implements Vehicle {
@Override
public void run() {
System.out.println("택시가 달립니다.");
}
}
<실행 코드>
public class DriverExample {
public static void main(String[] args) {
Driver driver = new Driver(); // 운전자 객체 생성
Bus bus = new Bus(); // 구현 객체 생성
Taxi taxi = new Taxi();
driver.drive(bus); // 버스가 달립니다.
driver.drive(taxi); // 택시가 달립니다.
}
}
강제 타입 변환 ( + 객체 타입 확인)
<인터페이스>
interface Vehicle1 {
public void run();
}
<구현 클래스>
class Bus1 implements Vehicle1 {
@Override
public void run() { // 인터페이스의 추상 메소드를 재정의한 실체 메소드
System.out.println("버스가 달립니다.");
}
public void checkFare() { // 인터페이스에 없는 새로운 메소드 선언
System.out.println("승차요금을 체크합니다.");
}
}
<실행 코드>
public class VehicleExample {
public static void main(String[] args) {
Vehicle1 vehicle = new Bus1(); // 자동 형변환
vehicle.run(); // 실체 메소드가 실행됨
// vehicle1.checkFare(); // Vehicle 인터페이스에 없는 메소드 호출시 컴파일 에러
Bus1 bus = (Bus1)vehicle; // 강제 형변환
bus.run();
bus.checkFare(); // Bus 클래스에 있는 메소드이므로 호출 가능
}
}
<출력 결과>
버스가 달립니다.
버스가 달립니다.
승차요금을 체크합니다.
객체 타입 확인
- 버스 타입인지 택시 타입인지 모르는 상태에서 버스 타입으로 강제 형변환하면 ClassCastException 에러가 발생함
- 매개 값으로 들어올 때 어떤 구현 객체가 인터페이스 타입으로 변환되었는지 확인하고자함
- 인터페이스 타입(Vehicle) 으로 자동 형변환된 매개값을 다시 구현 클래스 타입 (Bus)로 강제 타입 변환하고자 할때 반드시 그 객체가 Bus타입인지 Taxi 타입인지 확인을 해야한다.
- if문을 이용하여 instanceof 연산자로 확인한다.
- 이것을 하는 근본적인 이유는 checkFare() 메소드가 Bus타입에만 있고 인터페이스 타입에는 없어서 이 메소드를 사용하고 싶다면 다시 Bus 타입으로 강제형변환을 해줘야한다. 그 과정에서 이렇게 확인 작업이 필요하다.
객체 타입 확인 예제 ( instanceof 연산자)
<객체 타입 확인>
class Driver {
public void drive(Vehicle vehicle) { // main 메소드에서 vehicle 자리에 Bus 객체와 Taxi 객체를 보내줄 예정
if(vehicle instanceof Bus) {
Bus bus = (Bus) vehicle; // 매개 값으로 Bus 객체가 들어온다면 강제 타입 변환 가능
bus.checkFare(); // Bus타입으로 강제형변환을 한 이유로서 Bus 클래스의 메소드를 쓰기위함이다.
// ((Bus)vehicle).checkFare(); // 이렇게 한 줄로 쓸 수도 있음
vehicle.run();
} else {
vehicle.run(); // 인터페이스의 메소드 호출
}
}
}
<실행 코드>
public class DriverExample {
public static void main(String[] args) {
Driver driver = new Driver(); // 운전자 객체 생성
Bus bus = new Bus(); // 구현 객체 생성
Taxi taxi = new Taxi();
driver.drive(bus); // 승차요금을 체크합니다. 버스가 달립니다.
// drive() 메소드의 매개 값으로 bus를 대입했으므로 Bus 타입으로 형변환 가능
driver.drive(taxi); // 택시가 달립니다.
// taxi는 Bus 타입으로 형변환 안되서 checkFare() 출력안됨
Vehicle vehicle = new Bus(); // 자동 형변환
}
}
<출력 결과>
승차요금을 체크합니다. // Bus 객체가 들어가서 형변환 가능하여 if문 안의 checkFare() 메소드 실행된 결과임
버스가 달립니다. // Bus 객체가 들어가서 형변환 가능하여 if문 안의 run() 메소드 실행된 결과임
택시가 달립니다. // Taxi 객체가 들어가서 형변환 불가능하여 else문 안의 run() 메소드 실행된 결과임
인터페이스 상속
- 인터페이스끼리도 상속이 가능하다.
- 클래스의 상속과 달리 인터페이스 상속은 다중 상속이 가능하다.
- 구현 클래스는 하위 인터페이스를 구현하는 것 일지라도 상위 인터페이스의 실체 메소드까지 모두 가지고 있어야 한다.
- 따라서 하위 인터페이스를 구현하더라도 상위 인터페이스까지 new 연산자를 통해 객체를 생성하여 자동 타입 변환이 가능하다.
- 하위 인터페이스로 타입이 변환되면 상위 인터페이스의 모든 메소드까지 전부 사용 가능하다.
- 상위 인터페이스로 타입 변환이 되면 하위 인터페이스에 선언된 메소드는 사용 불가
인터페이스 상속 예제
<상위 인터페이스>
interface InterfaceA {
public void methodA();
}
interface InterfaceB {
public void methodB();
}
<하위 인터페이스>
interface InterfaceC extends InterfaceA, InterfaceB {
public void methodC();
}
<하위 인터페이스 구현>
class ImplementationC implements InterfaceC {
public void methodA() { // InterfaceC와 그 위에 있는 모든 상위 인터페이스들의 실체 메소드들도 있어야함
System.out.println("ImplementationC-methodA() 실행");
}
public void methodB() {
System.out.println("ImplementationC-methodB() 실행");
}
public void methodC() {
System.out.println("ImplementationC-methodC() 실행");
}
}
<실행 클래스>
public class Example {
public static void main(String[] args) {
ImplementationC impl = new ImplementationC();
InterfaceA ia = impl;
ia.methodA();
System.out.println(); // methodA()만 호출 가능
InterfaceB ib = impl;
ib.methodB();
System.out.println(); // methodB()만 호출 가능
InterfaceC ic = impl;
ic.methodA(); // InterfaceC 변수는 세 메소드 모두 호출 가능
ic.methodB();
ic.methodC();
}
}
'👨🏫Study > JAVA' 카테고리의 다른 글
[JAVA] 09 - 2 익명 객체 (1) | 2022.03.21 |
---|---|
[JAVA] 09 - 1 중첩 클래스와 중첩 인터페이스 소개 (0) | 2022.03.21 |
[JAVA] 08 - 1 인터페이스 (0) | 2022.03.21 |
[JAVA] 07 - 3 추상 클래스 (0) | 2022.03.21 |
[JAVA] 07 - 2 타입 변환과 다형성 (0) | 2022.03.21 |
댓글