08-2 타입 변환과 다형성

 

인터페이스에도 메소드 재정의와 타입 변환 기능을 제공해 다형성을 구현하는데에 상속과 더불어 많이 사용된다.

- 상속은 같은 종류의 하위 클래스 생성

- 인터페이스는 사용 방법이 동일한 클래스를 만드는 기술

이라는 개념상 차이가 있으나 구현 방법은 비슷하다 

(공부할때 상속과 헷갈릴 수 있다.)

 

프로그램 소스 코드는 변함이 없는데, 구현 객체를 교체함으로써 프로그램의 실행 결과가 다양해진다.
이것이 인터페이스의 다향성이다. 

자동 타입 변환

자동 타입 변환은 프로그램 실행 중에 자동적으로 타입이 변환 되는 것을 말하며

구현 객체가 인터페이스 타입으로 변환 되는 것은 자동 타입 변환(Promotion)에 해당한다. 

인터페이스 변수 = 구현 객체;

 

인터페이스 구현 클래스를 상속하여 자식 클래스를 만들었다면 자식 객체 또한 인터페이스 타입으로 변환이 가능하다.

이런 다양한 객체를 넣을 수 있는 인터페이스 변수를 필드와 매개체로 사용 한다면 다양한 실행결과를 만들 수 있다.

 

 


필드의 다형성

필드 값을 대입할 때 자동 타입 변환이 일어나며 다형성을 보여준다.

 

상속에서의 예시에서는 타이어 클래스 타입에 한국 타이어와 금호 타이어라는 자식 객체로 대입해 교체할 수 있음을 보여줬다.

하지만 인터페이스에서의 타이어는 인터페이스 타입으로 한국 타이어와 금호 타이어는 구현 클래스에 해당하여, 타이어 인터페이스를 구현했다. 따라서 타이어  인터페이스에 있는 메소드를 가지고 있어 타이어 인터페이스로 동일하게 사용할 수 있는 교체 가능한 객체가 된다는 것이 다르다.

 

  • 예시 (클래스때 예시가 유사한데 다른 부분을 찾으며 공부하면 좋다)
인터페이스 Tire roll() 추상메소드
구현 클래스 1 HankookTire roll() 
구현 클래스 2 KumhoTire roll() 

 

Class Car{

// 일반 클래스에서는 이런식으로 사용했으나 인터페이스는 그 자체로 객체 생성이 불가능하다
//    Tire frontLeftTire = new Tire();
//    Tire frontRightTire = new Tire();
//    Tire backLeftTire = new Tire();
//    Tire backRightTire = new Tire();

// 인터페이스는 구현 객체를 대입함으로써 사용이 가능하다
   Tire frontLeftTire = new HankookTire();
   Tire frontRightTire = new HankookTire();
   Tire backLeftTire = new KumhoTire();
   Tire backRightTire = new KumhoTire();

    void run(){
        frontLeftTire.roll();
        frontRightTire.roll();
        backLeftTire.roll();
        backRightTire.roll();
    }
}

 

이중 앞왼쪽 바퀴와 뒤오른쪽 바퀴를 각각 다른 타이어로 교체를 하면

인터페이스 타입인 타이어에 구현 객체인 한국타이어와 금호타이어가 들어가게 된다.

Car myCar = new Car();
myCar.frontLeftTire = new HankookTire();
myCar.backRightTire = new KumhoTire();
myCar.run();

객체 생성후 초기값으로 대입한 구현 객체 대신 다른 구현 객체도 대입이 가능하다. 한국타이어와 금호 타이어 모두 인터페이스의 추상메소드를 구현하고 있기 때문이다. 따라서 Car객체는 타이어 인터페이스에 선언된 메소드만 사용하면 되므로  어떤 객체가 저장되더라도 수정할 필요가 없다.

 

Car객체가 호출한 인터페이스 메소드에 각 저장된 구현 객체의 메소드를 호출하므로 인터페이스 변수로 선언된 필드는 저장된 객체에 따른 다형성을 보여줄 수 있게 된다. 

 

 


매개 변수의 다형성

메소드 호출할 때에도 자동타입 변환이 일어난다. 

상속에서는 매개변수를 부모 타입으로 선언하고 호출할 때 자식 객체를 대입했다면 

이번에는 매개변수를 인터페이스 타입으로 선언하고 호출할 때는 구현 객체를 대입한다.

 

매개변수의 타입이 인터페이스라면 이를 구현한 객체라면 어떠한 것도 매개값으로 사용할 수 있고, 어떤 객체냐에 따라 메소드의 실행 결과가 다양해 질 수 있다. 이를 매개 변수의 다형성이라고 한다.

 

예시

더보기

인터페이스

public interface Vehicle{
	public void run();
}

매개 변수를 인터페이스 타입으로 준 메소드

public class Driver{
	public void drive(Vehicle vehicle){
    	vehicle.run();
    }
}

구현 클래스 1

public class Bus implements Vehicle{
	@Override
    public void run(){
    	System.out.println("버스가 달립니다.");
    }
}

구현 클래스 2

public 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);	// 자동 타입 변환 Vehicle vehicle = bus;
        driver.drive(taxi);	// 자동 타입 변환 Vehicle vehicle = taxi;
    }
}
// 버스가 달립니다.
// 택시가 달립니다.

 

 

 


강제 타입 변환

구현객체가 인터페이스 타입으로 자동 변환되면 인터페이스에 선언된 메소드만 사용 가능하다는 제약이 생긴다.

구현 클래스에 메소드가 A,B,C,D,E 5개 있고 인터페이스에 선언된 메소드가 A,B,C 3개라면 자동 타입 변환시 호출 가능한 건 A,B,C 3개 뿐인 것이다. 

 

하지만 때에 따라서는 인터페이스에 없는 구현 클래스의 나머지 메소드, 필드도 사용해야할 경우가 있다. 이때 강제 타입 변환(Casting)을 해서 다시 구현 클래스 타입으로 변환 후 구현 클래스의 필드와 메소드를 사용할 수 있다.

구현클래스 변수 = (구현클래스) 인터페이스변수;

 

+상속과 비슷

 

 


객체 타입 확인

강제 타입 변환은 구현 객체가 인터페이스 타입으로 변환되어 있는 상태에서 가능하다. 

그렇지 않은 경우를 강제 타입 변환하면 ClassCastException이 발생한다. 

 

그렇다면 어떤 구현 객체가 인터페이스 타입으로 변환되었는지 어떻게 확인할 수 있을까

상속에서와 마찬가지로 instanceof 연산자를 이용하면 된다.

 

좌항에 인터페이스 타입, 우항에 구현 클래스를 두어 좌항이 우항에서 변한 것인 지를 확인 할 수 있다.

맞다면 true를 반환하고, 틀리다면 false를 반환한다.  

if(vehicle instanceof Bus){
	Bus bus = (Bus) vehicle;
}

이렇게 어떤 객체인지 확인을 하고 안전하게 강제 타입 변환을 해야 한다. 

 

 


인터페이스 상속

 

인터페이스도 다른 인터페이스를 상속할 수 있다. 그리고 일반 클래스와 달리 다중 상속이 가능하다. 

public interface 하위인터페이스 extends 상위인터페이스1, 상위인터페이스2, 상위인터페이스3...{...}

 

하위 인터페이스를 구현하는 클래스는 하위는 물론 상위의 모든 추상 메소드에 대한 실체 메소드를 가지고 있어야 한다. 그리고 이렇게 다 가지고 있기 때문에 아래와 같이 자동 변환이 가능하다. 

하위인터페이스 변수 = new 구현클래스();
상위인터페이스1 변수 = new 구현클래스();
상위인터페이스2 변수 = new 구현클래스();
상위인터페이스3 변수 = new 구현클래스();
..

 

하지만 하위인터페이스 변수로 변환되면 하위는 하위를 포함해 상위에 선언된 모든 메소드들을 사용할 수 있으나

만약 상위인터페이스1 변수로 변환되면 하위의 메소드들은 물론 상위2, 상위3에 선언된 메소드들을 사용할 수 없고 오로지 상위1의 메소드만 사용할 수 있다. 

+ Recent posts