2013-04-29 2 views
1

Предположим, у меня есть GrandParentClass, который наследуется ParentClass, который наследуется ChildClass.Попытка понять кастинг и динамическое связывание с точки зрения наследования

Почему я могу использовать только «цепочку», но не вниз?

Почему я могу только динамически связывать «вниз по цепочке», но не вверх?

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

Пример литья:

ParentClass object = new ParentClass(); 
System.out.println((GrandParentClass)object); //casting ChildClass throws compiler error 

Пример динамического связывания:

GrandParentClass = new ChildClass(); //Switching GrandParentClass with ChildClass throws run time error. 
+0

Возможно, по той же причине не существует множественного наследства? (потому что он быстро становится неуправляемым) – donfede

+0

Для вопроса о литье это хороший ответ: http://stackoverflow.com/questions/380813/downcasting-in-java – Shilpam

ответ

1

Общее рассуждение для этих литейных правил (не только в Java) является то, что наследование позволяет строить деревья классов, а не только списки. Иными словами, не каждый объект типа GrandParentClass обязательно является объектом типа ParentClass, и не каждый объект типа ParentClass обязательно имеет тип ChildClass. Вы можете представить альтернативу ParentClass2, наследующую от GrandParentClass, или просто создать экземпляр объекта типа GrandParentClass. Представьте, что произойдет, если вам будет разрешен доступ к объекту GrandParentClass, как если бы это был объект ChildClass со всеми методами и переменными, которые определены только в ChildClass, но не выше. Нет значимого разрешения.

Таким образом, литье «вверх по цепи» всегда имеет смысл, так как отношение наследования известно во время компиляции. Ваш динамический пример привязки - это одно и то же; Вы привязываете «вверх по цепочке» (совсем не «вниз по цепочке»), так как вы назначаете объект производного типа на ссылку типа, из которого он был получен.

+0

Я немного смутился о вас, говоря, что динамическое связывание по-прежнему «вверх» цепочку ", но в сочетании с ответом Сэмни, который имеет смысл. –

0

ParentClass имеет способ getWidget; ChildClass имеет унаследованный метод getWidget и объявлен новый метод getAnotherWidget. Это означает, что когда компилятор видит, что у вас есть объект ChildClass, он знает, что ChildClass может делать все, что может сделать ParentClass, поэтому можно безопасно обрабатывать ChildClass как ParentClass. Однако, когда компилятор видит ParentClass, он не знает, что ParentClass имеет метод getAnotherWidget - вы можете определить это во время выполнения (например, instanceof ChildClass), или во время компиляции вы можете сообщить компилятору «этот объект ParentClass действительно ChildClass - поверьте мне, я знаю, что я делаю », выполнив приказ ChildClass childClass = (ChildClass)parentClass - если вы ошибаетесь, вы получите ClassCastException. Вам нужно выполнить этот приведение, даже если ChildClass просто унаследовал от ParentClass без добавления каких-либо дополнительных методов - компилятор недостаточно умен, чтобы знать, что ChildClass в основном эквивалентен ParentClass.

Динамическое связывание работает только в одном направлении, потому что ParentClass не знает всех своих детей - вы можете загружать дополнительные классы наследования во время выполнения. Напротив, ChildClass знает, кто его ParentClass, и поэтому он может переопределить методы ParentClass. (Вот почему ChildClass может позвонить super.getWidget(), но ParentClass не может позвонить child.getWidget() - даже если ParentClass знал, кто все его дети, как он узнает, к какому классу детей вы обращались?Вместо того, чтобы перегружать синтаксис, для Java требуется упростить создание объекта ChildClass, если вы хотите использовать его методы, а не называть их через ParentClass)

+0

Что вы подразумеваете под словом «если вы ошибаетесь, тогда вы получите» исключение. Как то, что делает его правильным, чтобы иметь возможность бросить? –

+0

Если у вас есть 'ChildClass1' и' ChildClass2', которые оба наследуются от 'ParentClass', тогда вы получите' ClassCastException', если вы попытаетесь применить 'ChildClass1' к' ChildClass2' или наоборот, например. 'ParentClass parentClass = new ChildClass1(); ChildClass2 childClass = (ChildClass2) parentClass; 'будет генерировать исключение; это потому, что у 'ChildClass2' могут быть методы, которые несовместимы с' ChildClass1' –

+0

Когда вы бросаете объект, вы фактически не изменяете базовый объект, который неизменен для жизни объекта. Все, что вы делаете, говорит компилятору относиться к объекту так, как если бы он принадлежал другому классу; если вы скажете, чтобы он рассматривал объект как совместимый класс (например, родительский класс или дочерний класс), тогда все гаснет без сучка и задоринки, но если вы сообщите компилятору о том, что объект будет рассматриваться как несовместимый класс, тогда вы получите исключение. –

0

Динамическое связывание и литье идут рука об руку. Они, как я понимаю, это ALWAYS читать справа налево, чтобы выяснить, если вы можете связать и литая ... например:

object A                 ←             object B
                                                      является

2

Может быть легко думать об этом, как животные. Допустим, класс grandparent равен Animal, родительский класс - Dog, а дочерний класс - GermanShepherd.

Ваш первый пример будет как таковой:

Dog dog = new Dog(); 
System.out.println((Animal)dog); 
//casting GermanShepherd throws compiler error 

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

Ваш второй пример:

Animal animal = new GermanShepherd(); 
//Switching Animal with GermanShepherd throws run time error. 

Аналогично, немецкая овчарка всегда будет животное, но животное не может быть немецкая овчарка (может быть черепахой).

+1

Этот ответ потрясающий ... lol –