Notice
Recent Posts
Recent Comments
Link
«   2025/04   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30
Archives
Today
Total
관리 메뉴

나는개발자니까

[Java] Chapter 7.2 타입 변환과 다형성 본문

Java

[Java] Chapter 7.2 타입 변환과 다형성

된다고했잖아요 2023. 4. 10. 22:45

다형성의 개요

다형성은 사용 방법은 동일하지만 다양한 객체를 이용해서 다양한 실행결과가 나오도록 하는 성질입니다.

예를 들어, .. 다시 작성

 

다형성을 구현하려면 메소드 재정의와 타입 변환이 필요합니다.

 

자동 타입 변환

타입 변환은 타입을 다른 타입으로 변환하는 것을 말합니다.  여기서 클래스도 자동 타입 변환을 할 수 있고, 클래스의 변환은 상속 관계에 있는 클래스 사이에서 발생합니다.  

자동 타입 변환은 프로그램 실행 도중에 자동적으로 타입 변환이 일어나는 것입니다.

 

부모타입 변수 = 자식타입;

 

* 다형성과 관련된 자동 타입 변환의 특징

1. 변수가 부모 타입으로 자동 타입 변환된 이후에는 부모 클래스에 선언된 필드와 메소드만 접근이 가능합니다. 

2. 변수는 자식 객체를 참조하지만 변수로 접근한 멤버는 부모 클래스 멤버로만 한정됩니다. 

3. 메소드가 자식 클래스에서 재정의되었다면 자식 클래스의 메소드가 호출됩니다. 

 

근데 여기서 의문이 들었습니다.  처음부터 자식 타입을 사용하면 되지 굳이 부모타입으로 선언을 하고 자동 타입 변환을 할까?  이 내용은 다음 필드의 다형성과 연결됩니다.

필드의 다형성

필드의 다형성은 필드의 타입을 부모 타입으로 선언하면 다양한 자식 객체들이 저장될 수 있기 때문에 필드 사용 결과가 달라질 수 있습니다.

 

다형성을 구현할 수 있는 기술적 조건

1. 부모 클래스를 상속하는 자식 클래스는 부모가 가지고 있는 필드와 메스드를 가지고 있어 사용방법이 동일합니다.

2. 자식 클래스는 부모의 메소드를 재정의해서 메소드의 실행 내용을 변경함으로써 더 우수한 실행결과로 이끌 수 있습니다.

3. 자식 타입을 부모 타입으로 변환할 수 있습니다.

 

필드 다형성의 흐름

1. Car 클래스는 4개의 Tire 필드를 가지고 있습니다.  Car 클래스로부터 Car 객체를 생성하면 4개의 Tire 필드에 각각 하나씩 Tire 객체가 들어갑니다.

class Car {
	// 필드
    Tire frontLeftTire = new Tire();
    Tire frontRightTire = new Tire();
    Tire backLeftTire = new Tire();
    Tire backRightTire = new Tire();
    
    // 메소드
    void run() {
    ...
    }
}

 

2. 여기서 frontRightTire와 backLeftTire를 HankookTire와 KumhoTire로 교체할 이유가 생겼습니다.  Tire 클래스 타입인 frontRightTire와 backLeftTire에 Tire 자식 객체가 저장되어야 합니다.  하지만 문제가 되지 않습니다.

첫째, 앞에서 배웠던 자동 타입 변환으로 인해 자식타입이 부모타입으로 변환되기 때문에 Tire의 자식 객체가 저장되어도 됩니다.

둘째, frontRightTire와 backLeftTire에 Tire 자식 객체가 저장되어도 Car 객체는 Tire 클래스에 선언된 필드와 메소드만 사용하므로 전혀 문제가 되지 않습니다.  HankookTire와 KumhoTire는 부모인 Tire의 필드와 메소드를 가지고 있기 때문입니다.

Car myCar = new Car();
myCar.frontTightTire = new HankookTire();
myCar.backLeftTire = new KumhoTire();
myCar.run();

 

3. Car 객체에 run( ) 메소드가 있고, run( ) 메소드는 각 Tire 객체의 roll( ) 메소드를 호출합니다.  HankookTire와 KumhoTire로 교체되면 HankookTire와 KumhoTire가 roll( ) 메소드를 재정의하고 있으므로 교체 이후에는 HankookTire와 KumhoTire의 roll( ) 메소드가 호출되어 실행 결과가 달라집니다.

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

 

자동 타입 변환을 이용해서 Tire 필드값을 교체함으로써 Car의 run( ) 메소드를 수정하지 않아도 다양한 roll( ) 메소드의 실행결과를 얻을 수 있습니다. 

 

즉, 자식 타입을 부모 타입으로 자동 타입 변환을 함으로써 클래스의 메소드를 수정하지 않아도 다양한 실행 결과를 얻을 수 있습니다.

 

이것이 다형성의 강력한 점이고 필드의 다형성이라고 할 수 있습니다.

 

매개변수의 다형성

앞에서 우리는 필드의 값을 대입할때 자동 타입 변환이 되는 것을 배웠고, 이것을 통해 다형성을 배웠습니다. 

지금까지는 메소드를 호출할 때 매개 변수의 타입과 동일한 매개값을 대입하는 것을 일반적으로 했습니다.  하지만, 매개값을 다양화하기 위해 매개 변수에 자식 객체를 지정할 수 있습니다.

 

매개변수 다형성의 흐름

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

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

 

1 ) 우리가 일반적으로 아는 정상적인 호출

Driver driver = new Driver();
Vehicle vehicle = new Vehicle();
driver.drive(vehicle);

2 ) 다른 객체를 매개변수로 사용

Driver driver = new Driver();
Bus bus = new Bus();
driver.drive(bus);  // 자동 타입 변환 : Vehicle vehicle = bus;

 

매개변수의 타입이 클래스일 경우, 해당 클래스의 객체뿐만 아니라 자식 객체까지도 매개값으로 사용할 수 있다는 것이다.  매개값으로 어떤 자식 객체가 제공되느냐에 따라 메소드의 실행결과는 다양해질 수 있습니다.  자식 객체가 부모의 메소드를 재정의했다면 메소드 내부에서 재정의된 메소드를 호출함으로써 메소드의 실행결과는 다양해집니다.

 

강제 타입 변환

강제 타입 변환은 부모 타입을 자식 타입으로 변환하는 것을 말합니다.  

자식 타입이 부모 타입으로 자동 타입 변환한 후 다시 자식 타입으로 변환할 때 강제 타입 변환을 사용 할 수 있습니다.

// 자식 타입 변수 = (자식 타입) 부모 타입;
// 부모 타입을 자식 타입으로 변환
Parent parent = new Child();  // 1. 자동 타입 변환
Child child = (Child) parent;  // 2. 강제 타입 변환

 

1. 자동 타입 변환 : 자식 타입이 부모 타입으로 자동 타입 변환하면, 부모에 선언된 필드와 메소드만 사용 가능하다는 제약 사항이 따릅니다.

2. 자식에 선언된 필드와 메소드를 꼭 사용해야한다면 강제 타입 변환을 해서 다시 자식 타입으로 변환한 다음 자식의 필드와 메소드를 사용하면 됩니다.

 

객체 타입 확인

강제 타입 변환은 처음부터 부모 타입으로 생성된 객체는 자식 타입으로 변환할 수 없습니다.

Parent parent = new Parent();
Child child = (Child) parent; // 불가능

 

이때 부모 변수가 참조하는 객체가 부모 객체인지 자식 객체인지 확인하기 위해 instanceof 연산자를 사용합니다.

메소드 내에서 강제 타입 변환이 필요할 경우 반드시 매개값이 어떤 객체인지 instanceof 연산자로 확인하고 강제 타입 변환을 해야한다.

 

package sec02.exam06;
public class InstanceofExample {
	public static void method1(Parent parent) {
    	if (parent instanceof Child) {
        	Child child = (Child) parent;
            System.out.println("method1 - Child로 변환 성공");
        } else {
        	System.out.println("method1 - Child로 변환 성공");
        }
    }
    
    public static void method2(Parent parent) {
    	Child child = (Child) parent;
        System.out.println("method2 - Child로 변환 성공");
    }
    
    
    public static void main(String[] args) {
    	Parent parentA = new Child();
        method1(parentA);
        method2(parentA);
        
        Parent parentB = new Parent();
        method1(parentB);
        method2(parentB);
    }
}

 

1. InstanceofExample 클래스에서 method1( )과 method2( )를 호출할 경우, Child 객체를 매개값으로 전달하면 두 메소드 모두 예외가 발생하지 않는다.

2. Parent 객체를 매개값으로 전달하면 method2( ) 에서는 ClasscastException이 발생합니다.

3. method1( )은 instanceof 연산자로 변환이 가능한지 확인한 후 변활을 하지만, method2( )는 무조건 변환하려고 하기 때문이다.

4. 예외가 발생하면 프로그램이 즉시 종료되기 때문에 instanceof 연산자를 꼭 써야합니다.