Откровенный кастинг от супер класса до подкласса

StackOverflow https://stackoverflow.com/questions/4862960

Вопрос

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(); И это была бы собака), виртуальная машина бросает исключение во время выполнения, потому что вы нарушили это доверие (вы сказали компилятору, что все будет в порядке, а это не так!)

Компилятор немного умнее, чем просто вслепую, все принимая все, если вы попытаетесь отбрасывать объекты в разных иерархиях наследования (например, подставьте собаку на строку), то компилятор бросит его обратно на вас, потому что он знает, что это никогда не может работать.

Поскольку вы, по сути, просто мешаете компилятору жаловаться, каждый раз, когда вы бросаете, важно проверять, что вы не вызотите ClassCastException используя instanceof в операторном порядке (или что -то в этом роде.)

Другие советы

Потому что теоретически Animal animal Можно Будь собакой:

Animal animal = new Dog();

Как правило, удручение - не очень хорошая идея. Вы должны избежать этого. Если вы используете его, вам лучше включить чек:

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

Чтобы избежать такого рода ClassCastException, если у вас есть:

class A
class B extends A

Вы можете определить конструктор в B, который принимает объект A. Таким образом, мы можем сделать «актер», например:

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 в Runtime знает, что на самом деле является объектом.

Поэтому, когда JVM во время выполнения выясняет, что Dog d фактически ссылается на Animal а не Dog Объект это говорит. Эй ... ты солгал компилятору и бросает большой жир ClassCastException.

Так что, если вы спускаетесь, вы должны использовать instanceof Проверьте, чтобы не облагаться.

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

Теперь на нас возникает вопрос. Почему, черт возьми, разрешает падение, когда в конечном итоге он бросит java.lang.ClassCastException?

Ответ заключается в том, что все, что может сделать компилятор, - это убедиться, что эти два типа находятся в одном и том же дереве наследования, поэтому в зависимости от того, какой код мог бы прийти до сдачи, возможно, что, что animal имеет тип dog.

Компилятор должен разрешить вещи, которые могут работать во время выполнения.

Рассмотрим следующий код Snipet:

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

В отличие от Downcasting, сборы в скидке неявно, потому что, когда вы переверните, вы неявно ограничиваете количество методов, которое вы можете вызвать, как противоположность удналению, что подразумевает это позже, вы можете вызвать более конкретный метод.

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

Оба вышеупомянутого впадения будут работать нормально без каких-либо исключений, потому что собака-животное, которое может сделать животное, собака может сделать. Но это не правда Vica-versa.

Код генерирует ошибку компиляции, потому что ваш Тип экземпляра животное:

Animal animal=new Animal();

Уничтожение не допускается в Java по нескольким причинам. Видеть здесь Для деталей.

Как объяснено, это невозможно. Если вы хотите использовать метод подкласса, оцените возможность добавить метод в SuperClass (может быть пустым) и вызовите из подклассов, получая поведение, которое вы хотите (подкласс) благодаря полиморфизму. Поэтому, когда вы позвоните D.method (), вызов преуспеет в кастинге, но в случае, если объект не будет собакой, не будет проблемы

Чтобы разработать ответ @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);
    }
}

И вот результат:

Отец поле: отец строка

Детское поле: детская строка

Теперь вы можете легко добавить новые поля в класс отца, не беспокоясь о кодах ваших детей, чтобы сломать;

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top