Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is upcasting a Class not changing overridden methods? [duplicate]

I have a subclass ScottishPerson which inherits from the class BritishPerson.

class BritishPerson {
    public String name = "A british name";

    public void salute() {
        System.out.println("Good Morning!");
    }
}

class ScottishPerson extends BritishPerson {
    public String name = "A scottish name "; //Variable overriding
    public String clanName = "MacDonald";

    public void salute() //Method overriding
    {
        System.out.println("Madainn Mhath!");
    }

    public void warcry() {
        System.out.println("Alba Gu Brath!");
    }
}

class Driver {

    public static void main(String[] args) {
        ScottishPerson scottishPerson = new ScottishPerson(); //Created as a subclass, can always be upcasted.
        BritishPerson britishPerson = new BritishPerson(); //Created as the superclass, throws an error when downcasted.
        BritishPerson britishPersonUpcasted =
                new ScottishPerson(); //Created as the subclass but automatically upcasted, can be downcasted again.

        //Checking the methods and parameters of scottishPerson
        scottishPerson.salute();
        scottishPerson.warcry();
        System.out.println(scottishPerson.name);
        System.out.println(scottishPerson.clanName);

        //Checking the methods and parameters of britishPerson
        britishPerson.salute();
        System.out.println(britishPerson.name);

        //Checking the methods and parameters of britishPersonUpcasted
        britishPersonUpcasted.salute();
        System.out.println(britishPersonUpcasted.name);
    }
}

Running the code, this is the output.

Madainn Mhath!
Alba Gu Brath!
A scottish name 
MacDonald
Good Morning!
A british name
Madainn Mhath!
A british name

This is where the confusion lies. Upcasting ScottishPerson to BritishPerson changes the variable name into the one defined un the superclass. Methods and variables that exist only in the subclass such as warcry() and clanName are discarded. However, calling method salute() on the upcasted class still returns the string based on the subclass implementation.

Is it because when I created the object britishPerson I ONLY initialize the BritishPerson class, and that when I created the object britishPersonUpcasted I created both the BritishPerson class and ScottishPerson class which lead to the permanent overriding of the salute() method?

like image 630
TheValars Avatar asked Feb 08 '23 07:02

TheValars


2 Answers

The object on which you are actually calling method belongs to ScottishPerson so at compile time it check for the reference variable but the runtime it always executes the methods which belongs to the object not to reference which holding it. The runtime polymorphism actually lie behind this concept.

like image 141
Gaurav Jeswani Avatar answered Feb 13 '23 23:02

Gaurav Jeswani


Please have a look at this question for more understanding of upcast and downcast:

What is the difference between up-casting and down-casting with respect to class variable

I also made an example for looking at upcast behavior:

abstract class Animal 
{ 
    public void saySomething()
    {
        System.out.println("Some Animal sound");
    }

    public abstract void getTheBall();
}

class Horse extends Animal
{ 
    public void saySomething()
    {
        System.out.println("Neigh Neigh");
    }

    public void getTheBall()
    {
        System.out.println("I won't, Try a dog, I am a Horse!");
    }
}

class Dog extends Animal 
{ 
    public void saySomething()
    {
        System.out.println("woof woof, waon waon");
    }

    public void getTheBall()
    {
        System.out.println("huf huf, here it is!");
    }
}

public class Main 
{
    public static void main (String [] args) 
    {
        Dog dog = new Dog(); 
        Horse horse = new Horse();
        Animal animal = dog;
        Animal horseAnimal = new Horse();

        //upcasting
        Dog upcastedAnimal = upcastToDog(animal);
        dog.saySomething();
        dog.getTheBall();

        upcastedAnimal.saySomething();
        upcastedAnimal.getTheBall();

        horse.saySomething();
        horse.getTheBall();

        try {
            Dog upcastedDog = upcastToDog(horseAnimal);
        } catch (Exception ex){
            System.out.println(ex.getClass().getSimpleName() + ": Obviously a horse is not a dog!");
        }
    }

    public static Dog upcastToDog(Animal animal){
        return (Dog) animal;
    }
}

Output:

woof woof, waon waon
huf huf, here it is!
woof woof, waon waon
huf huf, here it is!
Neigh Neigh
I won't, Try a dog, I am a Horse!
ClassCastException: Obviously a horse is not a dog!

First of all java will throw exception if incompatible types are trying to be casted.

In the case when cast is possible, overriden methods will always be called from the actual instance. In your case instance is ScottishPerson so methods will be called on ScottishPerson, even if you hold its reference in a BritishPerson.

you can run the example here https://repl.it/B83f/3

In JLS it is covered here "Narrowing Reference Conversion", as he name suggests only reference is narrowed or widened (or upcasted or downcasted) not the instance.

like image 35
cpz Avatar answered Feb 14 '23 00:02

cpz