programing

슈퍼클래스에서 서브클래스로의 명시적 캐스팅

firstcheck 2022. 7. 27. 22:02
반응형

슈퍼클래스에서 서브클래스로의 명시적 캐스팅

public class Animal {
    public void eat() {}
}

public class Dog extends Animal {
    public void eat() {}

    public void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = (Dog) animal;
    }
}

★★Dog dog = (Dog) animal;오류를 시 합니다.ClassCastException컴파일러가 이 에러를 검출할 수 없는 이유는 무엇입니까?

깁스를 사용한다는 것은 컴파일러에게 "나를 믿어라"라고 말하는 것입니다. 하고 수는 이건animal"라고 말합니다."

를 할 수 .Animal animal = new Dog();(고객이 신뢰관계를 위반했기 때문에 VM이 런타임에 예외를 발생시킵니다) (컴파일러에게 모든 것이 정상이고, 그렇지 않다고 말씀하셨습니다!)

컴파일러는 모든 것을 맹목적으로 받아들이는 것보다 조금 더 똑똑합니다.여러분이 다른 상속 계층에서 오브젝트를 캐스팅하려고 하면(예를 들어 Dog to a String), 컴파일러는 그것이 결코 작동하지 않을 것을 알기 때문에 그것을 다시 당신에게 던집니다.

할 때마다 ''가 합니다.ClassCastException를 사용하여instanceof 스테이트먼트 그것)로 합니다.

이론적으로 말하면Animal animal 가 될 수 있다:

Animal animal = new Dog();

일반적으로 다운캐스팅은 좋은 생각이 아니다.피해야 해요.사용하는 경우는, 다음의 체크 마크를 붙이는 것이 좋습니다.

if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
}

이러한 종류의 ClassCastException을 피하기 위해 다음과 같은 경우:

class A
class B extends A

A의 개체를 사용하는 생성자를 B에서 정의할 수 있습니다.이렇게 하면 "캐스트"를 할 수 있습니다. 예:

public B(A a) {
    super(a.arg1, a.arg2); //arg1 and arg2 must be, at least, protected in class A
    // If B class has more attributes, then you would initilize them here
}

마이클 베리의 답변을 자세히 설명하다.

Dog d = (Dog)Animal; //Compiles but fails at runtime

여기에서는 컴파일러에게 다음과 같이 말합니다. '이렇게 하다'는 알고 있다d는, 는 「의」를 하고 있습니다.Dog'오브젝트'는 아니지만요.다운캐스트를 할 때 컴파일러는 우리를 믿어야 한다는 것을 기억하라.

컴파일러는 선언된 참조 유형만 인식합니다.런타임의 JVM은 개체의 실제 상태를 인식합니다.

실행 시이 "JVM"이 "JVM"을Dog d는, 는 「」, 「」를 하고 있습니다.Animal 한 푼도 없다Dog이의를 제기합니다.... 넌 컴파일러에게 거짓말을 하고 큰소리쳤잖아ClassCastException.

그래서 다운캐스트를 할 때는instanceof테스트에 실패하지 않도록 주의해 주세요.

if (animal instanceof Dog) { Dog dog = (Dog) animal; }

이제 우리 머릿속에 질문이 떠오른다.으로 Hell 다운캐스트를 왜 합니까?java.lang.ClassCastException

가 할 수 은 두 이 같은 트리에 입니다. 전에 "Downcast"가 될 수 따라서 다운캐스트 전에 어떤 코드가 나왔는지에 따라서는 다음 중 하나가 될 수 있습니다.animal은 「」입니다.dog

컴파일러는 실행 시 동작할 수 있는 것을 허용해야 합니다.

다음 코드 스니펫에 대해 생각해 보겠습니다.

public static void main(String[] args) 
{   
    Dog d = getMeAnAnimal();// ERROR: Type mismatch: cannot convert Animal to Dog
    Dog d = (Dog)getMeAnAnimal(); // Downcast works fine. No ClassCastException :)
    d.eat();

}

private static Animal getMeAnAnimal()
{
    Animal animal = new Dog();
    return animal;
}

그러나 컴파일러가 캐스트가 작동하지 않는다고 확신하면 컴파일은 실패합니다.즉, 다른 상속 계층에 객체를 캐스팅하려고 하는 경우

String s = (String)d; // ERROR : cannot cast for Dog to String

다운캐스트와는 달리 업캐스트는 암묵적으로 기능합니다.업캐스트를 할 때 호출할 수 있는 메서드의 수를 암묵적으로 제한하기 때문입니다.다운캐스트는 나중에 보다 구체적인 메서드를 호출하는 것을 의미합니다.

Dog d = new Dog(); Animal animal1 = d; // Works fine with no explicit cast Animal animal2 = (Animal) d; // Works fine with n explicit cast

위의 두 업캐스트 모두 개 IS-A Animal, Anithing an Animal이 할 수 있기 때문에 예외 없이 잘 작동합니다.하지만 그것은 진짜 vica-versa가 아닙니다.

@Caumons의 답변을 작성하려면:

한 아버지 반에 많은 아이들이 있고 그 반에 공통 분야를 추가할 필요가 있다고 상상해 보세요.전술한 접근방식을 고려할 경우 각 자녀 클래스로 하나씩 이동하여 새로운 필드의 컨스트럭터를 리팩터링해야 합니다.따라서 이 시나리오에서 그 해결책은 유망한 해결책이 아니다.

이제 이 솔루션을 살펴보겠습니다.

아버지는 각 자녀로부터 자기 물건을 받을 수 있다.다음은 아버지 클래스입니다.

public class Father {

    protected String fatherField;

    public Father(Father a){
        fatherField = a.fatherField;
    }

    //Second constructor
    public Father(String fatherField){
        this.fatherField = fatherField;
    }

    //.... Other constructors + Getters and Setters for the Fields
}

다음은 아버지 컨스트럭터 중 하나를 구현해야 하는 자녀 클래스입니다.이 경우 앞에서 언급한 컨스트럭터입니다.

public class Child extends Father {

    protected String childField;

    public Child(Father father, String childField ) {
        super(father);
        this.childField = childField;
    }

    //.... Other constructors + Getters and Setters for the Fields

    @Override
    public String toString() {
        return String.format("Father Field is: %s\nChild Field is: %s", fatherField, childField);
    }
}

이제 애플리케이션을 테스트합니다.

public class Test {
    public static void main(String[] args) {
        Father fatherObj = new Father("Father String");
        Child child = new Child(fatherObj, "Child String");
        System.out.println(child);
    }
}

그 결과는 다음과 같습니다.

아버지 필드: 아버지 스트링

하위 필드: 하위 문자열

이제 자녀 코드가 깨질 염려 없이 아버지 클래스에 새 필드를 쉽게 추가할 수 있습니다.

인스턴스 유형이 Animal이기 때문에 코드가 컴파일 오류를 생성합니다.

Animal animal=new Animal();

Java에서는 다운캐스팅이 허용되지 않는 몇 가지 이유가 있습니다.자세한 내용은 여기를 참조해 주세요.

설명대로 그것은 불가능합니다.서브클래스의 메서드를 사용하는 경우 슈퍼클래스에 메서드를 추가할 수 있는지 여부를 평가하여(공백일 수 있음), 다형성을 통해 원하는 동작을 얻을 수 있는 서브클래스로부터의 콜(서브클래스)을 실시합니다.따라서 d.method()를 호출하면 콜은 캐스팅으로 성공하지만 객체가 개가 아닌 경우에는 문제가 없습니다.

앞에서 설명한 바와 같이 오브젝트가 처음부터 서브클래스에서 인스턴스화되지 않는 한 슈퍼클래스에서 서브클래스로 캐스팅할 수 없습니다.

단, 회피책이 있습니다.

컨스트럭터 세트와 편리한 방법만 있으면 오브젝트를 Dog에 캐스팅하거나 동일한 Animal 속성을 가진 새로운 Dog 오브젝트를 반환할 수 있습니다.

다음으로 그 예를 제시하겠습니다.

public class Animal {

    public Animal() {}

    public Animal(Animal in) {
        // Assign animal properties
    }

    public Dog toDog() {
        if (this instanceof Dog)
            return (Dog) this;
        return new Dog(this);
    }

}

public class Dog extends Animal {

    public Dog(Animal in) {
        super(in);
    }

    public void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = animal.toDog();
    }

}

언급URL : https://stackoverflow.com/questions/4862960/explicit-casting-from-super-class-to-subclass

반응형