JAVA 타입변환과 다형성(인터페이스)
인터페이스의 다형성
프로그램 소스 코드는 변함이 없고 구현 객체를 교체함으로써 프로그램의 실행결과가 다양해집니다.
자동 타입 변환
구현 객체가 인터페이스 타입으로 변환되는 것은 자동 타입 변환에 해당합니다. 자동 타입 변환은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말합니다.
인터페이스 변수 = 구현 객체;
(자동 타입 변환)
인터페이스 구현 클래스를 상속해서 자식 클래스를 만들었다면 자식 객체 역시 인터페이스 타입으로 자동 타입 변환할 수 있습니다.
필드의 다형성
인터페이스
package ch08;
public interface Tire {
public void roll();
}
구현 클래스
package ch08;
public class HankookTire implements Tire {
@Override
public void roll() {
System.out.println("한국 타이어가 굴러갑니다.");
}
}
구현 클래스
package ch08;
public class KumhoTire implements Tire {
@Override
public void roll() {
System.out.println("금호 타이어가 굴러갑니다.");
}
}
필드 다형성
package ch08;
public class Car {
Tire frontLeftTire = new HankookTire();
Tire frontRightTire = new HankookTire();
Tire backLeftTire = new HankookTire();
Tire backRightTire = new HankookTire();
void run() {
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
backRightTire.roll();
}
}
필드 다형성 테스트
package ch08;
public class CarExample {
public static void main(String[] args) {
Car myCar = new Car();
myCar.run();
myCar.frontLeftTire = new KumhoTire();
myCar.frontRightTire = new KumhoTire();
myCar.run();
}
}
매개변수의 다형성
매개값을 다양화 하기 위해서 상속에서는 매개 변수를 부모타입으로 선언하고 호출할 때에는 자식객체를 대입했습니다.
이번에는 매개 변수를 인터페이스 타입으로 선언하고 호출할 때에는 구현 객체를 대입합니다.
매개 변수의 타입이 인터페이스일 경우 어떠한 구현 객체도 매개값으로 사용할 수 있고, 어떤 구현 객체가 제공되느냐에 따라 메소드의 실행결과는 다양해질 수 있습니다. 이것이 인터페이스 매개변수의 다형성 입니다.
매개 변수의 인터페이스화
package ch08;
public class Driver {
public void drive(Vehicle vehicle) {
vehicle.run();
}
}
인터페이스
package ch08;
public interface Vehicle {
public void run();
}
구현 클래스
package ch08;
public class Bus implements Vehicle {
@Override
public void run() {
System.out.println("버스가 달립니다.");
}
}
구현 클래스
package ch08;
public class Taxi implements Vehicle {
@Override
public void run() {
System.out.println("택시가 달립니다.");
}
}
다음은 Diriver클래스를 이용해서 실행하는 DriverExample 클래스입니다.
매개 변수의 다형성 테스트
package ch08;
public class DriverExample {
public static void mian(String[] args) {
Driver driver = new Driver();
Bus bus = new Bus();
Taxi taxi = new Taxi();
driver.drive(bus);
driver.drive(taxi);
}
}
강제 타입 변환
구현 객체가 인터페이스 타입으로 자동 타입 변환하면, 인터페이스에 선언된 메소듬나 사용 가능하다는 제약 사항이 따릅니다.
경우에 따라서는 구현 클래스에 선언된 필드와 메소드를 사용해야 할 경우도 발생합니다. 이때 강제 타입 변환을 해서 다시 구현 클래스 타입으로 변환한 다음, 구현클래스의 필드와 메소드를 사용할 수 있습니다.
강제 타입 변환
구현 클래스 변수 = (구현 클래스) 인터페이스 변수;
인터페이스
package ch08;
public interface Vehicle2 {
public void run();
}
구현 클래스
package ch08;
public class Bus2 implements Vehicle2 {
@Override
public void run() {
System.out.println("버스가 달립니다.");
}
public void checkFare() {
System.out.println("승차요금을 체크합니다.");
}
}
강제 타입 변환
package ch08;
public class VehicleExample {
public static void main(String[] args) {
// TODO Auto-generated method stub
Vehicle2 vehicle = new Bus2();
vehicle.run();
//vehicle.checkFare(); (x) vehicle 인터페이스에는 checkFare()가 없음
Bus2 bus = (Bus2) vehicle; // 강제 타입 변환
bus.run();
bus.checkFare();
}
}
객체 타입 확인
강제 타입 변환은 구현 객체가 인터페이스 타입으로 변환되어 있는 상태에서 가능합니다. 그러나 어떤 구현 객체가 변환되어 있는지 알 수 없는 상태에서 무작정 강제 타입으로 변환할 경우 ClassCastException이 발생할 수 도 있습니다.
예를들어 다음과 같이 Taxi 객체가 인터페이스로 변환되어 있을 경우, Bus 타입으로 강제 타입 변환하면 구현 클래스 타입이 다르므로 ClassCastException 이 발생합니다.
Vehicle vehicle = new Taxi();
Bus bus = (bus) vehicle;
우리는 상속에서 객체 타입을 확인하기 위해 instanceof 연산자를 사용했습니다. instanceof연산자는 인터페이스 타입에서도 사용할 수 있습니다.
객체 타입 확인
package ch08;
public class DriverExample2 {
public static void main(String[] args) {
Driver2 driver = new Driver2();
Bus2 bus = new Bus2();
Taxi taxi = new Taxi();
driver.driver(bus);
driver.driver(taxi);
}
}
인터페이스 상속
인터페이스도 다른 인터페이스를 상속할 수 있습니다. 인터페이스는 클래스랑 달리 다중 상속을 허용합니다.
다음과 같이 extends 키워드 뒤에 상속할 인터페이스들을 나열할 수 있습니다.
public interface 하위인터페이스 extends 상위인터페이스1, 상위인터페이스2 {...}
하위 인터페이스를 구현하는 클래스는 하위 인터페이스의 메소드 뿐만 아니라 상위 인터페이스의 모든 추상 메소드에 대한 실체 메소드를 가지고 있어야 합니다. 그렇기 때문에 구현 클래스로부터 객체를 생성한 후에 다음과 같이 하위 및 상위 인터페이스 타입으로 변환이 가능합니다.
하위인터페이스 변수 = new 구현클래스(...);
상위인터페이스1 변수 = new 구현클래스(...);
상위인터페이스2 변수 = new 구현클래스(...);
하위 인터페이스로 타입 변환이 되면 상위 및 하위 인터페이스에 선언된 모든 메소드를 사용할 수 있으나, 상위 인터페이스로 타입 변환되면 상위 인터페이스에 선언된 메소드만 사용 가능하고 하위 인터페이스에 선언된 메소드는 사용할 수 없습니다.
다음 예제를 통해 확인해 보겠습니다.
상위 인터페이스
package ch08;
public interface InterfaceA {
public void methodA();
}
상위 인터페이스
package ch08;
public interface InterfaceB {
public void methodB();
}
하위 인터페이스
package ch08;
public interface InterfaceC extends InterfaceA, InterfaceB {
public void methodC();
}
하위 인터페이스 구현
package ch08;
public class ImplementationC implements InterfaceC {
public void methodA() {
System.out.println("ImplementationC-methodA()실행");
}
public void methodB() {
System.out.println("ImplementationC-methodB()실행");
}
public void methodC() {
System.out.println("ImplementationC-methodC()실행");
}
}
호출 가능 메소드
package ch08;
public class Example {
public static void main(String[] args) {
ImplementationC impl = new ImplementationC();
InterfaceA ia = impl;
ia.methodA();
System.out.println(); // InterfaceA 변수는 methodA()만호출가능
InterfaceB ib = impl;
ib.methodB();
System.out.println();
InterfaceC ic = impl;
ic.methodA();
ic.methodB();
ic.methodC();
}
}