본문 바로가기

Programming/JAVA

상속(7) - 필드의 다형성 1

java logo image

 

 

 

앞서서 자바의 다형성에 대해 공부하면서, 클래스의 상속관계와 이에 따라서 부모 클래스로 타입의 자동 변환이 일어날 수 있고, 그럼에도 불구하고 자동 변환이 일어난 객체에서도 오버라이드를 통해 자식 클래스에서 재정의된 메서드까지 호출될 수 있음을 배웠습니다. 

 

짧게 한 단락으로 요약하였지만, 앞으로 배울 다형성에 관련된 성질들과 학습 내용은 이 전제 조건을 바탕으로 설명되기 때문에 잊지 말고 다시 한번 꼭 복습하시기를 바랍니다. 

 

 

 


 

 

이제부터는 이 다형성이라는 성질이 실제 프로그램 설계에 어떻게 이용되는지 구체적으로 살펴보도록 하겠습니다. 우선 대표적인 예시인 자동차의 타이어 사례를 중심으로 설명해 보겠습니다. 

 

기본적으로 자동차인 Car 클래스, 타이어인 Tire 클래스가 존재합니다. 그리고 Tire라는 클래스를 부모로 하여 상속받는 HankookTire클래스와 KumhoTire 클래스가 있습니다. Tire라는 클래스는 가장 기본적이고 특색없는 타이어로 차를 구매했을 때 디폴트로 장착된 타이어라고 가정하겠습니다. 그리고 그 기본 타이어가 펑크가 나거나 고장 났을 때는 HankookTire나 KumhoTire 중 선택하여 교체할 수 있죠. HankookTire나 KumhoTire는 Tire를 상속받는 자식 클래스이기 때문에, Car에 장착되기 위한 기본 조건은 모두 갖고 있습니다. 거기에 각각의 타이어에 맞는 특색 있는 속성과 성능을 가지고 있습니다. 

 

여기까지 이해 되셨다면, Car라는 클래스는 어떤 구조를 갖는지 설명하겠습니다. 기본적으로 Car는 필드와 메서드를 갖습니다. 메서드는 시동을 거는 run( )과 stop( ) 두 개의 메서드를 갖습니다. 그리고 여기서 중요한 Car의 필드에는 Tire클래스의 객체를 선언하게 됩니다. 자동차에는 네 개의 타이어가 장착되기 때문에, 총 네 개의 Tire 타입의 객체를 선언하게 되지요. 이를 간단히 코드로 살펴보겠습니다(구조만을 살펴보기 위한 단순화 코드이므로, 실제 작동 코드는 따로 작성하겠습니다!)

 

public class Car {
	// 필드
	Tire frontLeftTire = new Tire();
	Tire frontRightTire = new Tire();
	Tire rearLeftTire = new Tire();
	Tire rearRightTire = new Tire();
	
	// 메서드
	void run() { ... }
	void stop() { ... }
}

 

 

아마 run, stop 같은 메서드는 Tire 클래스 타입의 객체들이 갖고 있는 필드와 메서드를 활용하게 될 것으로 예상됩니다. 여기에 하나만 내용을 더해보겠습니다. 예를 들어 Car 클래스의 메서드 run( )을 실행시키면 필드에 선언한 네 개의 타이어 객체가 갖고 있는 roll( )이라는 메서드를 실행하도록 정의해 보겠습니다. 

 

public class Car {
	// 필드
	Tire frontLeftTire = new Tire();
	Tire frontRightTire = new Tire();
	Tire rearLeftTire = new Tire();
	Tire rearRightTire = new Tire();
	
	// 메서드
	void run() { 
		frontLeftTire.roll();
		frontRightTire.roll();
		rearLeftTire.roll();
		rearRightTire.roll();
	}
	void stop() { ... }
}

 

 

자, 여기저기 뭔가 빠진 부분들은 많지만 신경쓰지 말고 진행해 보죠. 이제 실행 클래스로 왔습니다. 실행 클래스에서는 Car라는 클래스의 인스턴스를 생성하여, Car 클래스가 가진 메서드 - 여기서는 run( )이 되겠네요 - 를 실행한다고 가정하겠습니다. 

 

그런데 여기서 하나 변동사항이 생겼습니다. myCar라는 Car 타입 객체를 생성하였는데, 이 myCar라는 객체가 가진 타이어 두 개를 교체해야 하는 상황이 생겼습니다. 그래서 myCar가 가진 frontRightTire와 backLeftTire를 각각 HankookTire와 KumhoTire 타입의 클래스로 변경하고자 합니다. 

 

앞서서 공부했듯이, Tire 클래스를 상속 받은 HankookTire / KumhoTire 클래스는 Tire 클래스 타입으로 자동 변환이 가능하므로 참조 인스턴스를 변동하는데 문제가 없습니다. 이를 코드로 구현하면 아래와 같습니다. 

 

public class ExampleMain {	
	public static void main(String[] args) {
		Car myCar = new Car();
		
		myCar.frontRightTire = new HankookTire();
		myCar.rearLeftTire = new KumhoTire();
		
		myCar.run();
	}
}

 

 

자, 위 실행 클래스를 잘 뜯어보면 myCar라는 Car 클래스 객체에 약간의 변화가 생겼습니다. Car 클래스의 필드 네 개 중 두 개의 필드 값의 참조가 변환되었죠? 하지만 여전히 Tire 클래스를 상속받은 클래스이기 때문에 작동에 문제가 없습니다. 또한, 여기서 KumhoTire와 HankookTire 클래스는 자식 클래스로서, roll( )이라는 Tire 클래스가 가진 메서드를 오버라이드해 재정의 해둔 상태라고 가정하겠습니다. 

 

그럼 frontRightTire와 rearLeftTire에서 실행되는 roll( ) 메서드는 각각의 자식 클래스가 재정의한 roll( ) 메서드를 실행하게 됩니다. 결과적으로 Car 클래스의 run( ) 메서드를 수정하지 않으면서도 Tire 클래스 타입을 사용한다는 정의는 그대로 두고, 참조하는 객체(부품)를 갈아 끼우는 형태만으로 다른 결과 값을 도출할 수 있게 되었습니다. 예를 들어 새로 장착한 두 개의 타이어는 메서드가 재정의 되어 내구성이 더 좋다든지, 혹은 안정성이 더 좋다든지 하는 성능의 차이를 발휘할 수 있게 되는 것이지요. 

 

우리는 다형성의 주요 특성을 요약하자면 "동일한 타입을 사용하지만, 다른 결과가 산출되는 성질"이라고 말했습니다. 즉, 표면적으로 "Tire"라는 클래스 타입의 필드를 사용하였지만, Tire를 상속 받은 자식 클래스를 사용함으로써 오버라이드 된 메서드를 호출하게 되고 이로 인해 다른 결과를 기대할 수 있게 된 것입니다. 이것이 다형성의 대표적인 예시입니다. 

 

다음 아티클에서는 개략적으로 설명한 위의 예시를 구체적인 메서드와 필드 선언을 통해 제대로 구현해 보도록 하겠습니다.