Transcripted Summary

The next principle of Object-Oriented programming that we'll learn about is polymorphism.

Polymorphism

Polymorphism is the ability to take multiple forms. Specifically, in object-oriented programming, polymorphism is where a subclass can define their own unique behaviors, and yet share some of the same behaviors of their superclass.

An example of polymorphism is where an object has a superclass type but is an instance of a subclass.

Here we have an object called dog and it's defined as type Animal but it is an instance of Dog.



We're able to do this because Dog inherits from Animal, and therefore dog is a Animal.

Let's demonstrate an example of polymorphism between an Animal, Dog, and Cat.

I've created a new class called Animal.

In this class, let's just make a simple method called makeSound() and it'll just print out a generic message.

# Animal.java

package chapter10;

public class Animal {

    public void makeSound(){
        System.out.println("unknown animal sound");
    }
}

Now I'm going to go into this Dog class, which is also new, and we're going to inherit from the Animal class.

And inside of here we're going to override that makeSound() method and we'll print something that's specific to the Dog.

In addition to the makeSound() method that we've inherited, let's also add a new method in here's that specific to the Dog called fetch().

# Dog.java

package chapter10;

public class Dog extends Animal {

    @Override
    public void makeSound(){
        System.out.println("woof");
    }

    public void fetch(){
        System.out.println("fetch is fun!");
    }
}

Okay.

Now let's go into the Cat class.

This will also inherit from Animal and will override the makeSound() method, and then add its own method.

# Cat.java

package chapter10;

public class Cat extends Animal {

    @Override
    public void makeSound(){
        System.out.println("meow");
    }

    public void scratch(){
        System.out.println("I am a cat. I scratch things.");
    }
}

Okay, now for demonstrating polymorphism.

Let's go into this new class called Zoo and we'll make a main method. Let's go ahead and create a normal Dog object for now.

package chapter10;

public class Zoo {

    public static void main(String[] args){
        Dog rocky = new Dog();
    }
}

Now this dog Rocky, he should be able to fetch because the fetch() method is inside of Dog. And then rocky should also be able to make a sound, right?

public static void main(String[] args){

    Dog rocky = new Dog();
    rocky.fetch();
    rocky.makeSound();
}

And since we overrode the makeSound() method in the Dog class, that's the one that will be executed.

Let's try it.



Yep, “fetch is fun!” and the makeSound() is the one from the Dog class, not the Animal class. So that's just normal.


Now I want to show you about the polymorphism part.

I can specify this type as Animal, and let's call this dog sasha. And I'm going to instantiate sasha as a Dog.

Animal sasha = new Dog();
sasha.makeSound();

So, sasha is of type Animal but an instance of Dog. Now when I do this, sasha can make a sound even though sasha is of type Animal, because we overrode this makeSound() method in the Dog class. So, that is the one that will be executed.

When we run this, we get “woof”, so, we see that it was the overridden method.

Let's continue on, we're going to actually change Sasha's type — this is real polymorphism.

Animal sasha = new Dog();
sasha.makeSound();

sasha = new Cat();
sasha.makeSound();

I can say sasha now is a Cat. So, I've changed Sasha from an instance of Dog to an instance of Cat.

Why is this legal?

Because they are both of type Animal, and since I've specified sasha as type Animal, then this is legal. So now we can make a sound. And now we should see two different sounds.

Yep. woof and meow.

Now, because sasha is of type Animal and not of type Cat, then sasha does not have access to the scratch() method. Because the scratch() method belongs to Cat, and yet sasha is of type Animal so we cannot make a direct call of sasha.scratch().

However, we can still get around this by casting.

Type Casting

Casting is the act of converting an object’s type into a different type.

If we did sasha.scratch(), notice here Sasha has been cast to type Cat.

Animal sasha = new Dog();
sasha.makeSound();

sasha = new Cat();
sasha.makeSound();
((Cat) sasha).scratch();

So, this is not changing the overall object — sasha is still of type Animal. But it's saying in this specific call go ahead and make sasha of type Cat, just so that we can execute this method.

And that worked as expected.


Now let's create a new method inside of this Zoo class to feed the animals.

public static void feed(Animal animal){

}

Now notice that this method feed() takes an Animal.

Technically rocky is a Dog, but yet we could still pass rocky into this feed() method.

Dog rocky = new Dog();
rocky.fetch();
rocky.makeSound();
feed(rocky);

The reason we're able to do that again is because of polymorphism. Because it accepts this superclass of Animal, that means we can pass in an Animal object or any subclass of Animal.

Let's do the same for sasha.

Animal sasha = new Dog();
sasha.makeSound();
feed(sasha);

Now within this method of feed(), let's say that I want to do different things based on the type. If it's actually a dog, I might want to give it dog food, and if it's a cat I want to give it cat food.

Even though our parameter is of type Animal, there's a way that we can still figure out exactly what the subclass is by doing a check.

I can use an operator that's called instanceof.

if(animal instanceof Dog){

}

instanceof Operator

instanceof will do a check to see if whatever's on the left side is actually an instance of whatever you specify on the right side.

So, I can check to see if this Animal is an instance of Dog. And this will return a boolean value. If true, then I know that this object was instantiated as type Dog.

Likewise, we can check for Cat.

if(animal instanceof Dog){
    System.out.println("here's your dog food");
}

else if(animal instanceof Cat){
    System.out.println("here's your cat food");
}

Okay, let's run it now and see what happens.

# Zoo.java

package chapter10;

public class Zoo {

    public static void main(String[] args){

        Dog rocky = new Dog();
        rocky.fetch();
        rocky.makeSound();
        feed(rocky);

        Animal sasha = new Dog();
        sasha.makeSound();
        feed(sasha);

        sasha = new Cat();
        sasha.makeSound();
        ((Cat) sasha).scratch();
        feed(sasha);
    }

    public static void feed(Animal animal){

        if(animal instanceof Dog){
            System.out.println("here's your dog food");
        }

        else if(animal instanceof Cat){
            System.out.println("here's your cat food");
        }
    }
}

Output:

fetch is fun!

woof

here's your dog food

woof

here's your dog food

meow

I am a cat. I scratch things.

here's your cat food
  • We see that the first call was to fetch(), and we got “fetch is fun” from the Dog class.

  • “woof” is from the overwritten makeSound method of the Dog class.

  • When passing rocky to feed(), we see that it did find that this was an instance of Dog.

  • For sasha who's of type Animal but an instance of Dog, we also see the makeSound() prints “woof”

  • It also feeds sasha dog food when we did the instanceof check.

  • When sasha became a cat, the makeSound prints “meow”.

  • Then we called the scratch().

  • This — “here’s your cat food” — was printed out because instanceof shows that it is a Cat.


# Key Points about Polymorphism

Here are the key points about polymorphism.



  • An object can have a superclass type and a subclass instance.
  • Polymorphic objects can only access the methods of their type, not of their instance, unless you use casting.
  • If a method is overwritten by the subclass, the polymorphic object will execute the overwritten method at runtime.
  • The instanceof operator is used to determine if an object is an instance of a certain class.



Optional Independent Exercise



For your optional assignment for this chapter, create a class called Fruit.

This class should contain a field called calories and a method called makeJuice(), which prints a statement — something like “juice is made” - just something generic.

Then create two subclasses of the Fruit class. For example, you can do Apple and Banana, or Orange and Lemon, whatever you would like to do.

And then create methods within these classes that are specific to them. So, if you're going to do the Apple class, then do something like removeSeeds(). If you're going to do the Banana class, then do something like peel().

And then set the calories within the constructors of these subclasses.

Override the makeJuice() method to print the specific type of juice that's going to be made.

And then finally create a Market class which tests polymorphism by creating several variations of these objects.

Good luck.

Solution

Programming can be done many different ways, but here’s my solution.



Resources