본문 바로가기
프로그래밍언어/Java

08-2 타입 변환과 다형성

by 스꼬맹이브로 2021. 6. 28.
728x90
반응형
SMALL

인터페이스 다형성이란?

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

 

<자동 타입 변환:promotion>

구현 객체가 인터페이스 타입으로 변환되는 것으로 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것을 말함

 

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

때문에 자동 타입 변환을 이용하면 필드의 다형성과 매개 변수의 다형성 구현이 가능하다.

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

 

<필드의 다형성>

필드의 다형성은 7장의 상속에서 다형성과 유사하다. 

상속과 다른 점은 타이어가 클래스 타입이 아닌 인터페이스 타입이라는 점과 타이어는 자식클래스가 아니라 구현 클래스라는 것이다. 

A사와 B사의 타이어는 공통적으로 타이어 인터페이스를 구현했기 때문에 모두 타이어 인터페이스에 있는 메소드를 가지고 있다. 따라서 타이어 인터페이스로 동일하게 사용할 수 있는 교체 가능한 객체에 해당한다.

 

다음은 위의 예시를 구현한 인터페이스 예제이다.

1. 인터페이스

public interface Tire {
	public void roll();
}

2. 구현 클래스1

public class ATire implements Tire {
	@Override
	public void roll() {
		System.out.println("A 타이어가 굴러갑니다.");
	}
}

3. 구현 클래스2

public class BTire implements Tire {
	@Override
	public void roll() {
		System.out.println("B 타이어가 굴러갑니다.");
	}
}

4. 필드 다형성

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

 

5. 필드 다형성 테스트

public class CarExample {
	public static void main(String[] args) {
		Car myCar = new Car();
		
		myCar.run();
		
		myCar.frontLeftTire = new BTire();
		myCar.frontRightTire = new BTire();
		
		myCar.run();
	}
}

결과 : 

4번에서 객체를 생성한 후, 초기값으로 대입한 구현 객체 대신 다른 구현 객체를 대입할 수 있으며 이것이 타이어 교체에 해당된다. 또한 frontLeftTire와 frontRigthTire에 어떠한 타이어 구현 객체가 저장되어도 Car 객체는 타이어 인터페이스에 선언된 메소드만 사용하므로 전혀 문제가 되지 않는다.

마지막으로 run()에서 frontTire의 roll()에서는 A사의 함수가 호출되고, backTire의 roll()에서는 B사의 함수가 호출된다.

이것이 필드의 다형성이다.

 

<매개 변수의 다형성>

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

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

예를 들어, Driver 클래스에 drive() 메소드가 정의되어 있는데 Vehicle 타입의 매개 변수가 선언되어 있다.

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

이때, Vehicle이 인터페이스 타입이라면 Bus 객체를 생성해서 매개값으로 줄 수 있다.

drive()메소드는 Vehicle타입을 매개 변수로 선언했지만, Vehicle을 구현한 Bus 객체가 매개값으로 사용되면 자동 타입 변환이 발생한다.

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

 

다음은 위의 클래스에서 Bus와 Taxi를 사용하여 매개 변수의 다형성을 보여주는 예시이다.

1. 매개 변수의 인터페이스화

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

2. 인터페이스

public interface Vehicle {
	public void run();
}

3. 구현 클래스1

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

3. 구현 클래스2

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

5. 매개 변수의 다형성 테스트

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;
	}
}

결과 :

 

<강제 타입 변환:casting>

구현 객체가 인터페이스 타입으로 자동 타입 변환하면, 인터페이스에 선언된 메소드만 사용 가능하다는 제약 사항이 따른다. 하지만 경우에 따라서 구현 클래스에 선언된 필드와 메소드를 사용해야 할 경우도 발생한다.

이럴 경우 강제 타입 변환을 해서 다시 구현 클래스 타입으로 변환한 다음, 구현 클래스의 필드와 메소드를 사용할 수 있다.

다음은 vehicle을 통한 강제 타입 변환 예시이다.

1. 인터페이스

public interface Vehicle {
	public void run();
}

2. 구현 클래스

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

3. 강제 타입 변환

public class VehicleExample {
	public static void main(String[] args) {
		Vehicle vehicle = new Bus();

		vehicle.run();
		//vehicle.checkFare(); (x)

		Bus bus = (Bus) vehicle;  //강제타입변환

		bus.run();
		bus.checkFare();
	}
}

결과:

 

<객체 타입 확인>

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

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

또한 어던 구현 객체가 지정될지 모르는 상황에서 매개값을 강제 타입 변환하면 classCastException이 발생할 수 있다.

때문에 어떤 구현 객체가 인터페이스 타입으로 변환되었는지 확인하기 위해 앞서 상속에서 객체 타입을 확인하기 위해 사용했던 instanceof 연산자를 사용할 수 있다.

예를 들어 Vehicle 인터페이스 타입으로 변환된 객체가 Bus인지 확인하려면 다음과 같이 작성하면 된다.

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

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

 

public class Driver {
	public void drive(Vehicle vehicle) {
		if(vehicle instanceof Bus) {
			Bus bus = (Bus) vehicle;
			bus.checkFare();
		}
		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);
		driver.drive(taxi);
	}
}

결과 :

728x90
반응형
LIST

'프로그래밍언어 > Java' 카테고리의 다른 글

09-2 익명 객체  (0) 2021.12.14
09-1 중첩 클래스와 중첩 인터페이스 소개  (0) 2021.06.29
08-1 인터페이스  (0) 2021.05.18
07-3 추상 클래스  (0) 2021.04.28
07-2 타입 변환과 다형성  (0) 2021.03.09