yeoseon/tip-archive

[Java] Casting

yeoseon opened this issue · 1 comments

업캐스팅과 다운캐스팅

상속관계에 있는 두 객체를 다운 캐스팅할 때
해당 로직에서 객체를 부모 객체로 new Instance를 하면 다운캐스팅 오류가 발생한다는 것을 몰랐다.
잘 모르고 기계적으로 처리하다보니 발생한 문제... 잘 알고 개발하자

따라서 부모 객체의 구현체에서 해당 부모 객체를 new instantce를 해야할 때에는, 이를 중간 매개체를 만들어서 자식 객체에서 new instance를 하도록 추가해줘야 한다.

캐스팅 (Casting)

형변환이라고 하다.
상속 관계에 있는 부모와 자식 클래스 간에는 서로 간의 형변환이 가능하다.

자료형이 정해진 변수에 값을 넣을때는 변수가 원하는 정보를 하나도 빠짐 없이 다 넣어줘야 성립한다.

관련이 있는 데이터들끼리만 형변환이 가능하다.

1. 상속관계가 맺어진 경우.
2. 인터페이스로 인해 확장이 된 경우.

업캐스팅(Upcasting)

image

자식 클래스부모 클래스의 타입으로 캐스팅된다.

Child 는 Parent 클래스를 상속 받으므로 Chiid 클래스가 Parent 클래스 보다 가지고 있는
데이터 양이 무조건 많다.
왜냐하면 Child 클래스는 적어도 Parent 클래스의 데이터를 가지고 있기 때문이다.

서브클래스는 수퍼클래스의 모든 특성을 상속받는다.
따라서 서브클래스는 수퍼클래스로 취급될 수 있다.

수퍼클래스 Reference 변수가 서브 클래스로 객체화된 인스턴스를 가리킬 수 있게 된다.
사람은 생물이다!

Parent parent = (Parent) new Child();

parent변수는 Parent 자료형 데이터 모두를 원한다.
new Child(); 라는 인스턴스는 Parent 자료형 데이터를 모두를 가지고 있을까?
대답은 Yes 이다. 왜냐하면 Child 클래스는 Parent 클래스를 상속 받았기 때문이다.
업캐스팅은 다형성 측면에서 (Parent)를 붙혀주지 않아도 동작한다.

// 캐스팅 후 멤버에 직접 접근 확인을 위해
// private 선언과 getter 메서드는 생략합니다.
class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}

class Student extends Person {
    String dept;

    public Student(String name) {
        super(name);
    }
}

public class CastingTest {
    public static void main(String[] args) {
        // 레퍼런스 student를 이용하면 name, dept에 접근 가능
        Student student = new Student("MadPlay");

        // 레퍼런스 person을 이용하면 Student 객체의 멤버 중
        // 오직 Person 클래스의 멤버만 접근이 가능합니다.
        Person person = student;
        person.name = "Kimtaeng";
        
        // 아래 문장은 컴파일 타임 오류
        person.dept = "Computer Eng";
    }
}

업캐스팅을 하게되면 객체 내에 있는 모든 멤버에 접근할 수 없습니다. 오직 수퍼 클래스의 멤버에만 접근이 가능합니다. 이는 필드뿐만 아니라 메서드(Method)에도 동일하게 적용된다.

다운캐스팅(Downcasting)

image

부모 클래스자식 클래스의 타입으로 캐스팅된다.

자신의 고유한 특성을 잃은 서브 클래스의 객체를 다시 복구시켜주는 것을 말한다.
업캐스팅된 것을 다시 원상태로 돌리는 것

Child child = new Parent();
컴파일 오류가 발생한다.
위에서 말한 변수가 원하는 정보를 다 채워줘야하는 원칙에 어긋난다.
child 변수가 원하는 정보는 Child 클래스의 데이터 전부를 원하는데,
Parent 인스턴스 ( new Parent(); ) 는 Parent 데이터만 가지고 있을 뿐,
Child의 데이터를 가지지 않는다. 그러므로 컴파일 오류가 발생한다.

Child child = (Child)new Parent();
런타임 오류가 발생한다.
JVM은 new Parent(); 인스턴스를 Child 데이터로 형변환하려고 했지만
Child 데이터는 만드는 프로그래머마다 성질이 다를 것인데, JVM은 Child 데이터가 무엇인지 모르기 때문이다.
기본자료형 끼리는 추리가 가능하지만 위와 같이 참조형 데이터 캐스팅에는 속성, 성질이 정해져 있지 않은 경우 JVM은 알 길이 없다.

Parent parent = new Child(); Child child = (Child)parent;

다음과 같은 경우 성립한다.
왜 성립이 되는것일까?
parent 변수는 Parent 클래스 형의 변수이지만, 태생이 Child 인스턴스인 데이터를 넣어주었다.
이런 parent 변수를 다시 Child 형태로 다운캐스팅을 했다.
parent 변수는 Parent 클래스형 상태이지만, 다운 캐스팅을 해주는 경우
( (Child)parent ) 태생이 Child 클래스 형이므로
JVM이 parent 변수는 태생 정보인 Child 클래스 데이터형으로 다운 캐스팅을 해줄 수 있는 것.

다운 캐스팅은 보통 성립하지 않는 문법이지만 업캐스팅이 선행된 경우 다운캐스팅이 성립되는 경우가 존재한다.

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}

class Student extends Person {
    String dept;

    public Student(String name) {
        super(name);
    }
}

public class CastingTest {
    public static void main(String[] args) {
        // 업캐스팅 선행
        Person person = new Student("MadPlay");

        // 다운캐스팅
        Student student = (Student) person;

        // Okay!
        student.name = "Kimtaeng";

        // Okay!
        student.dept = "Computer Eng";
    }
}

다운캐스팅의 혼동되는 객체를 구별하기 위해 도움을 주는 연산자가 있다.

instanceof 연산자

[instanceof] 참고(#125)

Reference