tags:

views:

286

answers:

9

I was explaining OOP to my friend. I was unable to answer this question. (How shameful of me? :( )

I just escaped by saying, since OOP depicts the real world. In real world, Parent's can accommodate children but children cannot accommodate parents. same is the case in OOP.I know its stupid. :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??

  }
}

Why isn't this statement valid?

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

since aChild's members are superset of aParent's members. Then why can't aChild accommodate a parent.

+10  A: 

Exactly because aChild is a superset of aParent's abilities. You can write:

class Fox : Animal

Because each Fox is an Animal. But the other way is not always true (not every Animal is a Fox).

Also it seems that you have your OOP mixed up. This is not a Parent-Child relationship, because there's no composition/trees involved. This is a Ancestor/Descendant inheritance relation.

Inheritance is "type of" not "contains". Hence it's Fox is a type of Animal, in your case it doesn't sound right -- "Child is a type of Parent" ? The naming of classes was the source of confusion ;).

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

Animal a = new Fox(); // ok!
Animal b = new Fish(); // ok!
Fox f = b; // obviously no!
Kornel Kisielewicz
Nice example but it would be a lot more instructive with a few lines of code: `Animal a = Fox; Fish f = a;`
Henk Holterman
Exactly, the best answer so far. +1
Adeel Ansari
claws
@Henk, thank you == corrected :)
Kornel Kisielewicz
+3  A: 

If it was valid, what would you expect when you read aChild.prop3? It is not defined on aParent.

Thomas Lötzer
A: 

I think you mean:

Child aChild = aParent;

You did not specify that aChild is of type Child.

The reference to a Child type will mean that you can call members on it that may no exist in the Parent. So, if you assign a Parent object to a Child reference, you will be able to call members that do not exist on the Parent.

Oded
+1  A: 

I would say your example is flawed in that Child extends from Parent, which doesn't really follow the "is-a" relationship particularly well. Far better to have a relationship whereby both Child and Parent inherit from a single base class: Person.

Using that approach it would be easier to explain to your friend why:

Person p = new Child();

... is valid, but the following is not:

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

It is precisely this reason why this assignment is disallowed in Java: What would the additional child fields be initialised with in this case?

Adamski
A: 

Semantically, inheritance denotes an “is a” relationship. For example, a bear “is a” kind of mammal, a house “is a” kind of tangible asset, and a quick sort “is a” particular kind of sorting algorithm. Inheritance thus implies a generalization/ specialization hierarchy, wherein a subclass specializes the more general structure or behavior of its superclasses. Indeed, this is the litmus test for inheritance: If B is not a kind of A, then B should not inherit from A. In your case it means, that Parent "is a" child, but not vice versa.

P.S. I think in this case you violates main inheritance principles.

Sergey Teplyakov
A: 

I think, you chose a wrong model for real-life Parents and Children ;) In real life, a Parent is always a Child and a Child can be a Parent.

If you turn it around, it works:

class Child {
  Child[] parents;
}

class Parent : Child {
  Child[] children;
}

A parent is-a child (of his/her own parents) and we can express:

Child aChild = aParent;

because every parent is a child as well, but not

Parent aParent = aChild;

because not all children are parents.

Andreas_D
A: 

If you take a parent class and extend it the class has all the features the parent class has plus some more.

If you assign an object of the type child to an object of the type parent like:

Parent aParent = aChild;

you reduce the interface of the child object to the features of the base class. This is perfectly ok because it means that some new features of the child aren't used in that context.

If you do it the other way round and try to cast a base class to a child you would end up with an object that could live up the expectations on its interface.

For example you define a base class like:

Child extends Parent

 void doSomeSpecialChildStuff...

Now you create a Parent and assign it to a child object.

Parent aParent = new Child()

Your programming language now thinks the aParent object is a Child. The problem is that now it would be perfectly valid to this:

 aParent.doSomeSpecialChildStuff()

Now you are calling a method that isn't defined for the object but the interface of the object says it is defined.

Janusz
A: 

Think "inheritance" = "specialisation", although this seems counter-intuitive at first. The set of "Childs" is a subset of the set of "Parents" (you see that your example is a bit misleading). It naturally follows that a variable that can 'hold' a "Child" cannot a hold an abitrary member of the "Parent" set because it may not be in it the "Child" subset. On the other way, a variable that can 'hold' a "Parent" can hold every member of the "Child" set.

There seems to be 2 ways of viewing inheritance. On the programming level, "Child" is a superset of "Parent" abilities, as Kornel said. But conceptually, "Child" is a specialisation of "Parent" thus representing only a subset of all possible "Parent". Think for example "Vehicle" and "Car": a Car is a special Vehicle. The set of all Cars is still a subset of all vehicles. You may even "do more" with a Car than with a general Vehicle (e.g. changing tires, fill in gasoline etc.) but it's still a Vehicle.

erik
A: 

The assignments you are performing are called downcasting and upcasting. Downcasting is what happens when you cast an object to a String. Upcasting is the opposite where you are casting a type to a more general type. Upcasting never fails. Downcasting is only valid up until the point where you are downcasting to a type more specific than what the object was instantiated as.

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. 
}
AaronLS