Почему ссылка на объект дочернего класса не может ссылаться на родительский объект класса?

Я объяснял ООП моему другу. Я не смог ответить на этот вопрос. (Как постыдно мне?:()

Я просто убежал, сказав, что ООП изображает реальный мир. В реальном мире родитель может разместить детей, но дети не могут приспособить родителей. То же самое происходит в ООП. Я знаю его глупость.: P

class Parent
{
  int prop1;
  int prop2;
}

class Child : Parent // class Child extends Parent  (in case of Java Lang.)
{
  int prop3;
  int prop4;

  public static void Main()
  {
     Child aChild = new Child();
     Parent aParent = new Parent();
     aParent = aChild;// is perfectly valid.
     aChild = aParent;// is not valid. Why??

  }
}

Почему это утверждение не верно?

 aChild = aParent;// is not valid. Why??

поскольку члены aChild являются надмножеством членов класса. Тогда почему не может aChild разместить родителя.

Ответ 1

Именно потому, что aChild - это супермножество способностей Paranent. Вы можете написать:

class Fox : Animal

Потому что каждый Фокс - это Животное. Но другой путь не всегда верен (не каждое Животное - Фокс).

Также кажется, что ваш ООП перепутал. Это не отношения родитель-ребенок, потому что нет состава/деревьев. Это отношение наследственного/наследственного наследства.

Наследование - это "тип", а не "содержит". Следовательно, Фокс - это тип животных, в вашем случае это звучит не так: "Ребенок - это тип Родитель"? Именование классов было источником путаницы;).

class Animal {}
class Fox : Animal {}
class Fish : Animal {}

Animal a = new Fox(); // ok!
Animal b = new Fish(); // ok!
Fox f = b; // obviously no!

Ответ 2

Если это было действительно, что вы ожидаете, когда будете читать aChild.prop3? Он не определен на aParent.

Ответ 3

класс "Ребенок" расширяет "Родительский"

"Объект дочернего класса по сути является родительским объектом класса

 Child aChild = new Child();
 Parent aParent = new Parent();
 aParent = aChild;// is perfectly valid.
 aChild = aParent;// is not valid.

в сегменте кода, как обычная операция присваивания, вышеуказанное считывается справа налево. строка 3 сегмента кода читает - "aChild (объект класса Child) является родительским" (из-за наследования дочерние объекты класса становятся объектами суперкласса по своей сути) таким образом, строка № 3 действительна.

тогда как в строке №4 он читает, "aParent (объект класса родителя) является дочерним" (наследование не говорит о том, что объекты суперкласса станут объектами дочерних классов, а наоборот) поэтому строка № 4 недействительна.

Ответ 4

Я бы сказал, что ваш пример испорчен тем, что Child простирается от Parent, что в действительности не соответствует особенностям "is-a". Намного лучше иметь отношения, при которых оба Child и Parent наследуются от одного базового класса: Person.

Используя этот подход, было бы проще объяснить вашему другу, почему:

Person p = new Child();

... действителен, но следующее:

// We do *not know* that the person being referenced is a Child.
Child c = person;

Именно поэтому это назначение не разрешено в Java: какие дополнительные дочерние поля будут инициализированы в этом случае?

Ответ 5

Думаю, вы имеете в виду:

Child aChild = aParent;

Вы не указали, что aChild имеет тип Child.

Ссылка на тип Child будет означать, что вы можете вызывать на нем членов, которые могут отсутствовать в Parent. Итак, если вы назначаете объект Parent в ссылку Child, вы сможете вызвать членов, которые не существуют в Parent.

Ответ 6

Семантически, наследование означает отношение "есть". Например, медведь ""вид млекопитающего, дом" является "своеобразным материальным активом, а быстрый вид" - особый вид алгоритма сортировки. Таким образом, наследование предполагает обобщение/ иерархии специализации, где подкласс специализируется на более общей структуре или поведение его суперклассов. Действительно, это лакмусовая бумажка для наследования: если B не является видом A, тогда B не должен наследовать от A. В вашем случае это означает, что родитель "является" ребенком, но не наоборот.

P.S. Я думаю, что в этом случае вы нарушаете основные принципы наследования.

Ответ 7

Я думаю, вы выбрали неправильную модель для реальных родителей и детей;) В реальной жизни родитель всегда ребенок, а ребенок может быть родителем.

Если вы включите его, он будет работать:

class Child {
  Child[] parents;
}

class Parent : Child {
  Child[] children;
}

Родительский is-a ребенок (его/ее собственных родителей), и мы можем выразить:

Child aChild = aParent;

потому что каждый родитель также является дочерним, но не

Parent aParent = aChild;

потому что не все дети являются родителями.

Ответ 8

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

Если вы назначаете объект типа child объекту типа parent, например:

Parent aParent = aChild;

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

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

Например, вы определяете базовый класс, например:

Child extends Parent

 void doSomeSpecialChildStuff...

Теперь вы создаете родительский элемент и назначаете его дочернему объекту.

Parent aParent = new Child()

Теперь ваш язык программирования считает, что объект aParent является дочерним. Проблема в том, что теперь это было бы совершенно верно:

 aParent.doSomeSpecialChildStuff()

Теперь вы вызываете метод, который не определен для объекта, но интерфейс объекта говорит, что он определен.

Ответ 9

Подумайте "наследование" = "специализация", хотя сначала это кажется встречным интуитивным. Набор "Childs" является подмножеством набора "Родители" (вы видите, что ваш пример немного вводит в заблуждение). Естественно, что переменная, которая может "удерживать" "ребенка", не может удерживать произвольного члена набора "Родитель", потому что в нем может быть не подмножество "Ребенок" . С другой стороны, переменная, которая может "удерживать" родительский элемент, может содержать каждый член набора "Child".

Кажется, есть два способа просмотра наследования. На уровне программирования "Ребенок" является надмножеством "родительских" способностей, как сказал Корнель. Но концептуально "Ребенок" - это специализация "Родитель", представляющая собой лишь подмножество всех возможных "Родительских". Подумайте, например, "Автомобиль" и "Автомобиль": автомобиль - это особый автомобиль. Набор всех автомобилей по-прежнему является подмножеством всех автомобилей. Вы можете даже "сделать больше" с автомобилем, чем с общим автомобилем (например, сменить шины, заполнить бензин и т.д.), Но это все еще транспортное средство.

Ответ 10

Назначения, которые вы выполняете, называются downcasting и upcasting. Downcasting - это то, что происходит, когда вы набрасываете object на String. Апробация - это наоборот, когда вы применяете тип к более общему типу. Ускорение никогда не терпит неудачу. Downcasting действует только до тех пор, пока вы не опуститесь до типа, более определенного, чем тот, который был создан объектом.

class Named
{
    public string FullName
}

class Person: Named
{
    public bool IsAlive
}

class Parent: Person
{   
   public Collection<Child> Children
}

class Child : Person 
{  
   public Parent parent;
}

public static void Main()
{
  Named personAsNamed = new Person(); //upcast
  Person person = personAsNamed ; //downcast
  Child person = personAsNamed ; //failed downcast because object was instantiated
  //as a Person, and child is more specialized than(is a derived type of) Person
  //The Person has no implementation or data required to support the additional Child
  //operations. 
}

Ответ 11

Если у меня есть класс, скажем

class A{
    getA(){

    }
}

class B extend A{
    getB(){

    }
}

Теперь class B знает два метода getA() и getB(). но class A знает только метод getA().

Итак, если class B obj = new class A(); мы сделали беспорядок, так как для class B допустимо ссылаться на методы getA() и getB(), но только getA(). Это объясняет проблему.

Это мое понимание того, что нельзя разрешить ссылку на класс дочернего класса для родительского класса.

Ответ 12

Ответ на кучу AaronLS имеет прекрасный технический смысл.

Ссылки хранятся в стеке, а объекты хранятся в куче. Мы можем назначить дочерний объект родительскому типу, потому что child является типом родительского, а дочерний объект имеет ссылку для родительского класса. Хотя родительский тип не является дочерним. Родительский объект не имеет ссылки на дочерний элемент, поэтому дочерняя ссылка can not указывает на родительский объект.

Вот почему мы можем использовать десятичное значение для int и int для десятичного. Но мы не можем отбросить родитель-ребенка в обоих направлениях. Поскольку родитель не имеет понятия о своих дочерних ссылках.

Int i = 5;
Decimal d = 5.5;

d = i;

or 

i = d;

Оба действительны. Но это не относится к ссылочным типам, которые хранятся в куче.

Ответ 13

Ссылки хранятся в стеке, а объекты хранятся в куче. Мы можем назначить дочерний объект родительскому типу, потому что child является типом родительского, а дочерний объект имеет ссылку для родительского класса. Хотя родительский тип не является дочерним. Родительский объект не имеет ссылки на дочерний элемент, поэтому дочерняя ссылка can not указывает на родительский объект.