[Java] 8-2 타입 변환과 다형성

Woohyun Shin·2021년 7월 29일
0

Java

목록 보기
5/10

프로그램을 개발할 때 인터페이스를 사용해서 메소드를 호출하도록 코딩하면 구현 객체를 매우 손쉽고 빠르게 교체할 수 있다.

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

자동 타입 변환

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

인터페이스 구현 클래스를 상속해서 자식 클래스를 만들었다면 자식 객체 역시 인터페이스 타입으로 자동 타입 변환할 수 있다.

자동 타입 변환을 이용하면 필드의 다형성매개 변수의 다형성을 구현할 수 있다.

필드와 매개 변수의 타입을 인터페이스로 선언하면 다양한 구현 객체를 대입해서 실행결과를 다양하게 만들 수 있다.

필드의 다형성

7장에서는 자동차를 예로 들어 타이어 클래스 타입에 자식 객체인 한국/금호 타이어를 대입해서 교체할 수 있음을 보여주었다.

이번 장에서는 한국/금호 타이어는 공통적으로 타이어 인터페이스를 구현했기 때문에 모두 타이어 인터페이스에 있는 메소드를 가지고 있다.(인터페이스로 동일하게 사용할 수 있는 교체가능한 객체에 해당)

package sec03.exam02;

public interface Tire { //roll() 메소드 호출 방법 설명
	public void roll();
}
package sec03.exam02;

public class HankookTire implements Tire {
	@Override //Tire 인터페이스 구현
	public void roll() {
		System.out.println("한국 타이어가 굴러갑니다.");
	}
}
package sec03.exam02;

public class KumhoTire implements Tire {
	@Override //Tire 인터페이스 구현
	public void roll() {
		System.out.println("금호 타이어가 굴러갑니다.");
	}
}
package sec03.exam02;

public class Car3 {
	
	//인터페이스 타입 필드 선언과 초기 구현 객체 대입
	Tire frontLeftTire = new HankookTire();
	Tire frontRightTire = new HankookTire();
	Tire backLeftTire = new HankookTire();
	Tire backRightTire = new HankookTire();
	
	//인터페이스에서 설명된 roll() 메소드 호출
	void run() {
		frontLeftTire.roll();
		frontRightTire.roll();
		backLeftTire.roll();
		backRightTire.roll();
	}
}
package sec03.exam02;

public class CarExample3 {

	public static void main(String[] args) {
		// TODO 자동 생성된 메소드 스텁
		Car3 myCar=new Car3();
		
		myCar.run();
		
		myCar.frontLeftTire = new KumhoTire();
		myCar.frontRightTire = new KumhoTire();
		
		myCar.run();

	}

}

한국 타이어가 굴러갑니다.
한국 타이어가 굴러갑니다.
한국 타이어가 굴러갑니다.
한국 타이어가 굴러갑니다.
금호 타이어가 굴러갑니다.
금호 타이어가 굴러갑니다.
한국 타이어가 굴러갑니다.
한국 타이어가 굴러갑니다.

매개 변수의 다형성

자동 타입 변환은 필드의 값을 대입할 때에도 발생하지만, 주로 메소드를 호출할 때 많이 발생한다.

7장 상속 파트에서는 매개값을 다양화하기 위해 매개 변수를 부모 타입으로 선언하고서 호출할 때는 자식 객체를 대입했듯, 이번에는 매개 변수를 인터페이스 타입으로 선언하고 호출할 때는 구현 객체를 대입한다.

매개 변수의 타입이 인터페이스일 경우 어떤 구현 객체도 매개값으로 사용할 수 있고, 어떤 구현 객체가 제공되느냐에 따라서 메소드의 실행결과는 달라진다. 이것이 '매개 변수의 다형성'이다.

package sec03.exam02;

public interface Vehicle {
	
	public void run();

}
package sec03.exam02;

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

public class Taxi implements Vehicle{
	@Override
	public void run() {
		System.out.println("택시가 달립니다.");
	}
}
package sec03.exam02;

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

}
package sec03.exam02;

public class DriverExample {

	public static void main(String[] args) {
		// TODO 자동 생성된 메소드 스텁
		Driver driver = new Driver();
		
		Bus bus = new Bus();
		Taxi taxi = new Taxi();
		
		driver.drive(bus); //자동 타입 변환 : Vehicle vehicle = bus;
		driver.drive(taxi); //자동 타입 변환 : Vehicle vehicle = taxi;

	}

}

버스가 달립니다.
택시가 달립니다.

강제 타입 변환

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

하지만 구현 클래스에 선언된 필드와 메소드를 사용해야 할 경우, 강제 타입 변환(Casting)을 해서 다시 구현 클래스 타입으로 변환하여 구현 클래스의 필드와 메소드를 사용할 수 있다.

package sec03.exam02;

public class Bus implements Vehicle{
	
	@Override
	public void run() {
		System.out.println("버스가 달립니다.");
	}
	
	public void checkFare() {
		System.out.println("승차요금을 체크합니다.");
	}
	
}
package sec03.exam02;

public class VehicleExample {

	public static void main(String[] args) {
		// TODO 자동 생성된 메소드 스텁
		
		Vehicle vehicle = new Bus();
		
		vehicle.run();
		//vehicle.checkFare(); (x)Vehicle 인터페이스에 checkFare()가 없음
		
		Bus bus = (Bus)vehicle; //강제 타입 변환
		
		bus.run();
		bus.checkFare(); //Bus 클래스에는 checkFare()가 있음

	}

}

버스가 달립니다.
버스가 달립니다.
승차요금을 체크합니다.

객체 타입 확인

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

그러나 어떤 구현 객체가 변환되어 있는지 알 수 없는 상태에서 무작정 강제 타입 변환할 경우 ClassCastException이 발생할 수 있다.

예를 들어 다음과 같이 Taxi 객체가 인터페이스로 변환되어 있을 경우, Bus 타입으로 강제 타입 변환하면 구현 클래스 타입이 다르므로 에러가 발생한다.

Vehicle vehicle = new Taxi();
Bus bus = (Bus) vehicle; //ClassCastException 발생

메소드의 매개 변수가 인터페이스로 선언된 경우 메소드를 호출할 때 다양한 구현 객체들을 매개값으로 지정할 수 있는데, 어떤 구현 객체가 지정될지 모르는 상황에서 다음과 같이 Bus로 강제 타입 변환하면 에러가 발생한다.

public void drive(Vehicle vehicle){
  Bus bus = (Bus)vehicle; ///ClassCastException 이 발생할 수 있음
  bus.checkFare();
  vehicle.run();
  }

우리는 상속에서 객체 타입을 확인하기 위해 사용했던 instanceof 연산자를 인터페이스 타입에서도 사용할 수 있다.

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

인터페이스 타입으로 자동 타입 변환된 매개값을 메소드 내에서 다시 구현 클래스 타입으로 강제 타입 변환해야 한다면 반드시 매개값이 어떤 객체인지 instanceof 연산자로 확인하고 안전하게 강제 타입 변환해야 한다.

package sec03.exam02;

public class Driver {
	
	public void drive(Vehicle vehicle) { 
		
		if(vehicle instanceof Bus) { //vehicle 매개 변수가 참조하는 객체가 Bus인지 검사
			Bus bus = (Bus)vehicle; //Bus객체일 경우 안전하게 강제 타입 변환
			bus.checkFare(); //Bus타입으로 강제 타입 변환하는 이유
		}
		vehicle.run();
	}

}
package sec03.exam02;

public class DriverExample {

	public static void main(String[] args) {
		// TODO 자동 생성된 메소드 스텁
		Driver driver = new Driver();
		
		Bus bus = new Bus();
		Taxi taxi = new Taxi();
		
		driver.drive(bus); //자동 타입 변환 : Vehicle vehicle = bus;
		driver.drive(taxi); //자동 타입 변환 : Vehicle vehicle = taxi;

	}

}

승차요금을 체크합니다.
버스가 달립니다.
택시가 달립니다.

profile
조급함보다는 꾸준하게

0개의 댓글