Minwoo Dev.

[Java] 다형성 - 다형적 참조, 다운캐스팅(Down Casting), 업캐스팅(Up Casting) 본문

Java

[Java] 다형성 - 다형적 참조, 다운캐스팅(Down Casting), 업캐스팅(Up Casting)

itisminu 2024. 4. 10. 13:21
728x90
반응형
SMALL

다형성(polymorphism)

 

한 객체가 여러 개의 객체로 취급될 수 있는 것을 의미한다.

 

상속받는 관계의 Parent와 Child 클래스를 만들어 보겠다.

Parent.java

package practice.poly;

public class Parent {
    public void pMethod(){
        System.out.println("This is Parent's method");
    }
}

 

 

Child.java

package practice.poly;

public class Child extends Parent{
    public void cMthod(){
        System.out.println("This is Child's method");
    }
}

 

Main.java

package practice.poly;

public class polyMain {
    public static void main(String[] args) {

        // 부모 변수에 부모 인스턴스 참조
        System.out.println("Parent 변수에 Parent 참조");
        Parent parent = new Parent();
        parent.pMethod();

        // 자식 변수에 자식 인스턴스 참조
        System.out.println("Child 변수에 Child 참조");
        Child child = new Child();
        child.cMthod();
        
    }
}

 

 

Child 클래스는 Parent 클래스를 상속받은 상태이다.

따라서 Child 객체를 생성하면 Child 인스턴스와 Parent 인스턴스가 함께 할당될 것이다.

 

Main의 코드를 보면,

1. 부모 변수에 부모 인스턴스 참조

일반적인 Parent 변수의 생성 과정과 동일하다.

parent 에 Parent 객체를 생성 하였으므로 parent.pMethod()는 Parent 클래스 내부의 메서드이다.

 

 

2. 자식 변수에 자식 인스턴스 참조

이 코드 또한 일반적인 Child 변수의 생성과정과 동일하다.

child에 Child 객체를 생성하였고, child.cMethod()는 당연히 Child 클래스로 생성한 객체의 메서드일 것이다.

 

package practice.poly;

public class polyMain {
    public static void main(String[] args) {

        // 3. 부모 변수가 자식 인스턴스 참조
        System.out.println("Parent 변수에 Child 참조");
        Parent poly1 = new Child();
        poly1.pMethod();
        poly1.cMehod(); // 에러 발생!

        // 4. 자식 변수가 부모 인스턴스 참조
        System.out.println("Child 변수에 Parent 참조");
        Child poly2 = new Parent(); // 에러 발생!
        poly2.cMthod();
        poly2.pMethod();

    }
}

 

이번에는 일반적으로 우리가 알던 방법이 아닌 두 타입을 섞어서 인스턴스를 선언해보겠다.

 

 

3. 부모 변수가 자식 인스턴스 참조

이 코드에서는 Child 인스턴스를 Parent에 넣고 있다.

그리고 에러가 발생하지 않는다. 

 

 

부모 변수를 자식 인스턴스에 참조하게 된다면 위와 같은 형식으로 참조가 진행된다.

자식은 부모를 상속하므로 Child 인스턴스는 Parent와 Child를 모두 가지고 있다.

그러므로 Parent 타입(변수)가 Child 인스턴스 내부의 Parent를 가리킬 수 있다.

따라서 부모 변수가 자식 인스턴스를 참조할 수 있게 되는 것이다.

 

쉽게 이야기해서, 부모는 자식을 품을 수 있다 는 문장으로 기억해두면 쉬울 거 같다.

 

하지만 변수 타입이 Parent이기 때문에 Child 인스턴스를 참조하였다 하더라도 실질적으로 Parent 부분만 접근이 가능하다.

따라서 poly.cMethod() 는 실행할 수 없다.

 

 

 

위처럼 같은 타입이 아닌 다른 타입에 다른 인스턴스를 참조하게 하는 방식을 다형적 참조 라고 한다.

 

하지만 다형적 참조를 사용하면 상속된 두 인스턴스 중에 일부만 사용하고 나머지는 사용하지 않는다.

어떻게 보면 잘려나갈 데이터인데 왜 굳이 Child로 인스턴스를 생성했는지 의문이 들 수 있다.

 

이 부분에 대한 의문은 오버라이딩과 다형적 참조를 활용하는 예시를 통해 이해할 수 있을 것이다.

 

4. 자식 변수가 부모 인스턴스 참조

이 코드는 결론부터 말하자면 에러가 발생하여 실행할 수 없다.

 


자식 클래스에는 extends와 같이 상속을 의미하는 코드가 있지만, 부모 클래스에는 자식 클래스에 대한 상속관계를 의미하는 코드가 존재하지 않는다.

 

따라서 부모 인스턴스를 생성한다면, Parent 객체 하나만 생성될 것이다.

3. 의 그림처럼 Parent와 Child가 모두 생성되지 않는다.

 

그러므로 자식 변수가 부모 인스턴스를 참조하면 Parent 인스턴스 뿐이라서 참조할 곳이 없다.

따라서 다형적 참조가 불가능하게 된다.

 

한마디로 자식은 부모를 품을 수 없다. 가 된다.

 

 

 

 

다운캐스팅(Down Casting)

"캐스팅" 이란 용어는 변수 사이의 형변환할 때 많이 사용했을 것이다.

예를 들어, 실수형 변수 double에 담긴 값을 정수형 변수 int에 담으려고 할 때, 데이터의 손실이 우려되기에 Java에서는 자동 형변환을 지원하지 않는다.

그럴 때 우리는 (int)를 앞에 붙여 강제 형변환을 진행하였었다.

 

이런 과정을 캐스팅(Casting)이라고 하는데, 이 캐스팅이 다형적 참조에도 사용된다.

 

위 예제의 3. 부모 변수가 자식 인스턴스 참조 를 보자

package practice.poly;

public class polyMain {
    public static void main(String[] args) {


        // 3. 부모 변수가 자식 인스턴스 참조
        System.out.println("Parent 변수에 Child 참조");
        Parent poly1 = new Child();
        poly1.pMethod();
        poly1.cMehod(); // 에러 발생!


    }
}

 

사진 구조로 아래와 같은 구조였다.

 

여기서, 우리는 poly.cMethod가 실행되지 않는다는 것을 알았다.

poly가 Parent 타입이기 때문에 Parent 인스턴스에 접근하기 때문이다.

이럴 때, 캐스팅을 사용하면 Child에도 접근할 수 있다.

 

package practice.poly;

public class polyMain {
    public static void main(String[] args) {


        // 3. 부모 변수가 자식 인스턴스 참조
        System.out.println("Parent 변수에 Child 참조");
        Parent poly1 = new Child();
        Child poly2 = (Child) poly1;
        poly2.pMethod(); // 접근 가능!!
        poly2.cMthod(); // 접근 가능!!

    }
}

 

Child 타입의 변수에 기존에 생성했던 poly1을 캐스팅하여 참조하도록 하였다.

(Child) 를 앞에 붙여 Parent 타입이었던 인스턴스 참조를 Child 타입으로 임시변환하여 새로운 변수에 참조한 것이다.

 

결과적으로 Child 타입의 변수에 Child 인스턴스가 들어가는 것이다.

자식 클래스는 부모 클래스의 상속을 명시한다. 

 

 

따라서 자식 인스턴스에서 부모 인스턴스로의 이동은 가능하다.

자식 인스턴스에 해당하는 메서드가 존재하지 않는다면, 상속받은 부모 인스턴스로 이동하여 값을 찾는다. 

따라서 poly2.pMethod() 는 Child 인스턴스에 존재하지 않기에 부모 인스턴스인 Parent 인스턴으로 올라가 찾은 결과이다.

 

 

이처럼 부모 클래스에서 자식 클래스로의 캐스팅을 다운캐스팅(Down Casting)이라고 한다.

 

 

다운캐스팅을 하면 위처럼 Child 인스턴스에 접근할 수 있게 된다.

 

 

 

업캐스팅(Up Casting)

 

업캐스팅은 자식 타입의 인스턴스를 부모 타입으로 바꿔주는 것이다. 

 

 

2. 자식 변수에 자식 인스턴스 참조 를 보자.

package practice.poly;

public class polyMain {
    public static void main(String[] args) {


        // 2. 자식 변수에 자식 인스턴스 참조
        System.out.println("Child 변수에 Child 참조");
        Child child = new Child();
        Parent parent1 = (Parent) child;

        parent1.pMethod();
        parent1.cMethod(); // 에러!


    }
}

 

위 코드는 Child 타입의 변수를 Parent 타입으로 캐스팅하는 예제이다.

자식 타입에서 부모 타입으로 변경하는 것이니 업캐스팅(Up Casting) 이라고 한다.

 

Child 타입이었던 변수는 Child 인스턴스와 Parent 인스턴스에 모두 접근할 수 있었지만, Parent 타입으로 캐스팅한 후에는 Child 인스턴스에는 접근하지 못하게 된다.

 

업캐스팅은 (타입) 형태를 사용하지 않고도 작성할 수 있다. (부모 타입은 자식 타입을 담을 수 있다. -> 위의 다형적 참조 내용과 동일)

 

package practice.poly;

public class polyMain {
    public static void main(String[] args) {


        // 2. 자식 변수에 자식 인스턴스 참조
        System.out.println("Child 변수에 Child 참조");
        Child child = new Child();
        Parent parent1 = child; // (Parent) 생략!

        parent1.pMethod();
        parent1.cMethod(); // 에러!


    }
}

 

생략을 해도 되고 안해도 괜찮다. 하지만 개발자들은 생략하는 것을 권장하고 있다.

 

 

 

이로써 우리는 Java의 다형성의 중요한 개념 중 하나인 다형적 참조와 캐스팅에 대하여 학습하였다.

 

 

 

 

 

 

 

자료 출처 - 인프런 강의 [김영한의 실전 Java]

728x90
반응형
LIST