Почему нельзя ссылаться на дочерний объект Class на родительский объект Class? - PullRequest
13 голосов
/ 27 января 2010

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

Я только что сбежал, сказав, что ООП изображает реальный мир. В реальном мире родители могут разместить детей, но дети не могут разместить родителей. То же самое и в ООП. Я знаю, что это глупо. : 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 - это расширенные члены aParent. Тогда почему ребенок не может разместить родителя.

Ответы [ 13 ]

33 голосов
/ 27 января 2010

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

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!
6 голосов
/ 27 января 2010

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

4 голосов
/ 19 октября 2012

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

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

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

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

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

2 голосов
/ 27 января 2010

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

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

Person p = new Child();

... допустимо, но следующее не является:

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

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

1 голос
/ 13 августа 2015

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

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() Это объясняет проблему.

Это мое понимание того, что недопустимо, чтобы дочерний класс содержал ссылку на родительский класс.

0 голосов
/ 02 января 2017

Ответ Heck-Stack от AaronLS имеет технический смысл.

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

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

Int i = 5;
Decimal d = 5.5;

d = i;

or 

i = d;

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

0 голосов
/ 14 ноября 2014

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

0 голосов
/ 27 января 2010

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

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. 
}
0 голосов
/ 27 января 2010

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

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

0 голосов
/ 27 января 2010

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

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

Parent aParent = aChild;

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

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

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

Child extends Parent

 void doSomeSpecialChildStuff...

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

Parent aParent = new Child()

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

 aParent.doSomeSpecialChildStuff()

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...