claws
claws

Reputation: 54140

Why can't reference to child Class object refer to the parent Class object?

I was explaining OOP to my friend. I was unable to answer this question.

I just escaped by saying, since OOP depicts the real world. In real world, parents can accommodate children but children cannot accommodate parents. same is the case in OOP.

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.

Upvotes: 23

Views: 35743

Answers (16)

AppliedK
AppliedK

Reputation: 1

  // a dog is a type of animal
  Animal animal = new Dog();//upcasting
  // animal is not a type of dog
  Dog dog = new Animal();// error

From the above comment, it is obvious that we are using a reference variable of some TYPE in LHS of assignment operator and object in RHS. We should thus check if the object in RHS is of the TYPE given in LHS. The type of object must be legal to the reference variable type. The above example helps to think the semantic meaning of assigning an object to a reference variable. It is not the technical details, just mnemonics to avoid mistake.

The OOP is based on real world and its syntax is semantically as per the real world. So, the given mnemonic works. Remember that language designers have provided syntax as per the real world. Think real world situation to avoid mistake.

Now I try to explain the convention that is behind the syntax.

  • First, we should not think that reference variable is assigned an object. Rather remember that reference variable of X type holds address of object of X type (or some type which is hierarchically related to X). Reference variable is on stack and object is in heap memory.
  • Second, Reference variable holds address of object but the address must be of a valid type. Just as we cannot assign string literal to int type, similarly, we cannot assign ANY/EVERY type object to a reference variable. The object given in RHS must be a valid type. So, child class reference variable can hold address of a valid type e.g. its own type or its subtype but not its parent type in hierarchy. Remember it as a rule or convention that we can assign same type or its subtype object to a reference variable. Assigning subtype object to reference variable is called upcasting.
  • You should ask yourself why there is type for a reference variable. A reference variable has a type so that it could refer an object of valid type. A child reference variable cannot point or refer to its supertype object because it would miss the information of its own members when referring to super type object.

Generally a reference variable x of type X refers to address of an object of type X i.e. X x = new X(); Likewise, Parent p = new Parent(); and Child c = new Child(); Let Child class inherits Parent class. So, when we assign Parent p = new Child(); It is called upcasting because child object is referenced by its parent type, not by its own type. But directly assigning type object to subtype reference variable is illegal. Child c = new Parent();//illegal

Upvotes: 0

mtmk
mtmk

Reputation: 6316

It's the compiler trying to stop you creating runtime bugs like calling a member of an object that doesn't exist. You maybe able to do such a thing on languages with less strict type systems like Perl or C but then you're on your own at runtime.

Compile time errors are better than runtime errors if you don't mind a few restrictions.

Upvotes: 0

Manish Kumar Pandit
Manish Kumar Pandit

Reputation: 1

Basically, it all comes as way of assignment, as we all are familiar with the data types available in programming languages.

let's take an example of numeric data types and it's assignment.

  1. byte can be easily assigned to short without casting.(byte b = 127; short s = b;)

  2. short can be easily assigned to int without casting.(short s = 32,767; int i = i;) and so on....

but when we try reverse way, i.e, int to short or short to byte. we get compile time error, then to fix this compile time error, we do type casting as follows below:

int a = 10; byte b = a; //compile time error ; this is because size of int is greater then int, so byte cannot accommodate to have int.

then we do type casting(i.e, value to be assigned gets converted into requested data type and then it gets assigned). i.e, byte b = (byte)a;

Similarly, when it comes to parent class object assigning to child class reference :

child class cannot accommodate to have parent class object, in case if it does also with the help of type casting, will be able to get rid of compile time exception, but will get runtime exception, which is because at run time, child variable will be holding the parent object and there could be possibility where child may request for some features which parent doesn't have.

Upvotes: 0

Anuj Sharma
Anuj Sharma

Reputation: 421

The Heap-Stack answer by AaronLS makes perfect technical sense.

References are store on stack while objects are store on heap. We can assign child object to parent type reference because child is type of parent and child object has reference for parent class. While parent is not of type child. Parent object doesn’t have reference to child so child reference can’t point to parent object.

This is the reason why we can cast decimal to int and int to decimal. But we can not cast parent-child both ways. Because parent has no clue about its children's references.

Int i = 5;
Decimal d = 5.5;

d = i;

or 

i = d;

Both are valid. But same is not the case with reference types which are stored on heap.

Upvotes: 2

Rajesh Nair
Rajesh Nair

Reputation: 53

If I have a class, say

class A{
    getA(){

    }
}

class B extend A{
    getB(){

    }
}

Now class B knows two methods getA() and getB(). but class A knows only getA() method.

So, if we have class B obj = new class A(); we have made a mess , as it is valid for class B to reference methods getA() and getB() but only getA() is valid. That explains the issue.

This is my understanding of not allowing child class hold reference of parent class.

Upvotes: 3

Anil
Anil

Reputation: 1857

References are store on stack while objects are store on heap. We can assign child object to parent type reference because child is type of parent and child object has reference for parent class. While parent is not of type child. Parent object doesn’t have reference to child so child reference can’t point to parent object.

Upvotes: -1

Pranavan Maru
Pranavan Maru

Reputation: 491

class "Child" extends "Parent"

"child class object is inherently a parent class object"

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

in a code segment like a normal assignment operation, the above is read from right to left. line 3 of the code segment reads - "aChild (a Child class object) is a Parent" (due to inheritence child class objects become superclass objects inherently) thus line no.3 is valid.

whereas in line no.4 it reads, "aParent (a Parent class object) is a child" (inheritence doesn't say that superclass objects will become child class objects. it says the opposite) thus line no.4 is invalid.

Upvotes: 6

AaronLS
AaronLS

Reputation: 38374

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

Upvotes: 0

Kornel Kisielewicz
Kornel Kisielewicz

Reputation: 57575

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!

Upvotes: 49

erik
erik

Reputation: 253

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.

Upvotes: 0

Janusz
Janusz

Reputation: 189544

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.

Upvotes: 0

Andreas Dolk
Andreas Dolk

Reputation: 114807

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.

Upvotes: 1

Sergey Teplyakov
Sergey Teplyakov

Reputation: 11657

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.

Upvotes: 0

Adamski
Adamski

Reputation: 54715

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?

Upvotes: 2

Oded
Oded

Reputation: 499172

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.

Upvotes: 0

Thomas L&#246;tzer
Thomas L&#246;tzer

Reputation: 25401

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

Upvotes: 9

Related Questions