Allo0o2a
Allo0o2a

Reputation: 45

Superclass obj = new subclass(); meaning and casting

If I have super class (Animal) and a sub class (Cat).

What does the third point mean? And when we have to cast?

  1. Cat obj = new Cat(); means creating an object from Cat class

  2. Animal obj = new Animal(); means creating an object from Animal class

  3. Animal obj = new Cat();

Upvotes: 1

Views: 819

Answers (2)

nits.kk
nits.kk

Reputation: 5316

First lets understand Class, reference and Object. Suppose we have a class named SomeClass

SomeClass ref = new SomeClass();

Above we have an Object of SomeClass created in Heap and a reference variable refers to it. We have named the reference variable as ref. Object is present in heap and we can just access it using a reference. So Object type is of the actual class (on which new keyword has been applied). Reference variable type can be of actual class or its Parent class.

Now let us see the relationship of Inheritance. A class inheriting from another class share a Child-Parent relationship. Child inherits the behaviour of its Parent and can then override some of the behaviour and also can add some additional behaviour. Hence Object of Child can be used at any place where Parent object is expected, as Child has all the behaviour of its Parent so invoking any behaviour present in the Parent will be handled by the Child.

Parent class do not know about the additional behaviour of its child class ( child class is written later in time.) Hence object of Parent can not be used at the places where Object of Child is expected (If additional behaviour of Child is invoked on Parent object then it will not be honoured).

Now let us assume we have classes ParentClass and ChildClass such that ChildClass inherits ParentClass

ParentClass reference =  new ParentClass(); // Valid
ParentClass reference = new ChildClass(); //Valid
ChildClass reference = new ChildClass(); //Valid
ChildClass reference = new ParentClass();// Not Valid.

Note that ParentClass reference = new ChildClass(); // Here Object is of type ChildClass and Reference is of type ParentClass.

Now when to cast. Any place expecting the object of ParentClass, there is no need to cast, both the objects (of ParentClass or of ChildClass) are fine. Any place expecting the Object of type ChildClass but if we have a case like below then casting is needed.

public void someMethod(ChildClass expected){
    //some implementation
}
ParentClass ref = new ChildClass();
someMethod(ref);//Invalid : Compilation Issue 
someMethod((ChildClass)ref);// Valid
ParentClass anotherRef = new ParentClass();
someMethod(anotherRef); // Invalid : Compilation Issue
someMethod((ChildClass)ref); //Invalid, compiles but Runtime it will fail. 

Thumb rule : Child is Child, Child is Parent, Parent is Parent , Parent is not Child.

Another example for understanding.

public abstract class List{
    public abstract void add(int element);
    public abstract void remove(int element);
   public int size();
}

public class Application{
    private  List listReference;
    public void setList(List ref){
         listReference = ref;
    }
}

//Now you may create sub classes as below 
public class ArrayList extends List{
     // all the abstract methods of List have been implemented 
}

public class LinkedList extends List{
    //all the abstract methods of List have been implemented
}

Now in main method you can pass ArrayList or LinkedList or any other implementation.

public class Init{
    public static void main(String[] args){
         Application app = new Application ();
         app.setList(new LinkedList());
         //or you can set it like this
         List listRef = bew ArrayList();
         app.setList(listRef);
         //or you can set it like this
         LinkedList linkedListRef = new LinkedLiet();
         app.setList(linkedListRef);
    }
}

Notice that the method setList() accepts List type of reference and we can provide any implementation of the List abstraction. This leads to a flexible design. Classes should be dependent on the abstraction. Programming to interface is a Design Principle which leads to easy maintenance of the application code.

Upvotes: 2

18grady
18grady

Reputation: 51

The reason why this is confusing on the face of it is that it is not something that you would typically do in real code, except in the case of a Factory.

As hinted at in Zabuza's comment, you can do this because a Cat 'is-a' kind of Animal and so you can assign an object of type Cat to an object of type Animal. But you can't do the assignment the other way of course, because an Animal is not a kind of Cat.

Now, there are some lurking issues that come with actually being able to create an instance of the the supertype as well as the subtype that mean you typically wouldn't actually do this in real code because it complicates a lot of things down the road. What you would more likely do is make Animal an interface and have a GenericAnimal class that implements it, along with having Cat implement it.

Say you have an object that represents a zoo, and most zoos typically have a collection of animals. The most obvious way to represent this would be this:

java.util.Collection<com.myproject.Animal> zooAnimals;

So now imagine the zoo builds a new habitat, and it's for a lion. For the sake of the story assume we have a very lazy data model and instead of having a specific com.myproject.animals.cats.Lion subtype we just said "lions are cats, close enough". So to update the data structure that tracks all the animals and their names and addresses and favorite foods and whatever else, we might do this:

com.myproject.Animal newArrival = new com.myproject.animals.Cat("Larry the Lion", "Africa Exhibit", "Gazelles");
zooAnimals.add(newArrival);

Now imagine that the zoo continues to grow, and gets an Ostrich in the Africa habitat. And the same lazy data model applies so we just call it a Bird.

com.myproject.Animal newArrival = new com.myproject.animals.Bird("Oliver the Ostrich", "Africa Exhibit", "Whatever Ostriches Eat");
zooAnimals.add(newArrival);

Now actually writing that exact code would normally only happen in very specific cases inside a factory object or something, and realistically type hierarchies like this have a tendency to not work very well in practice at all, contrary to what a lot of us learned in Object Oriented Programming class, but for the sake of the question that is an example situation where you might do what you are asking about.

Lastly, you also asked when you have to cast. You would have to do this if you had code that needed to know about any special methods or fields that the Cat or Bird types have that Animal does not have. For instance the Cat type might have a property called tailLength because cats typically have tails and for whatever reason the zoo likes to keep track of that. Similarly the Bird type might have a property called wingSpan because birds have wings and we want to keep track of how big they are. The Animal type doesn't have any of these properties so if we get the object for the lion or the ostrich out of the zooAnimals collection (and maybe we looked at the name or something to figure out it was the lion) we would have to cast back to the Cat type in order to access the tailLength property. Same thing for the ostrich and it's wingspan.

for( Animal theAnimal : zooAnimals ){
    if( theAnimal.getName().equals("Larry the Lion") ){
        Cat theCat = (Cat)theAnimal;
        System.out.println("Larry's tail is " + theCat.getTailLength() + " inches long";
    }
    else if( theAnimal.getName().equals("Oliver the Ostrich") ){
        Bird theBird = (Bird)theAnimal;
        System.out.println("Oliver's wingspan is " + theBird.getWingSpan() + " inches";
   }
}

Again you probably wouldn't actually do something like that in real code, but perhaps it helps to illustrate the example.

Upvotes: 0

Related Questions