javadynamic-bindingupcasting

confusion about upcasting vs dynamic binding


So I am having some confusion understanding dynamic binding vs upcasting.

My original understanding was that when you create a base_class reference and assign it to a derived class object like:

base_class obj = new derived_class();

you can now call the methods/member functions of derived_class with obj.method_of_derived class(), without dynamic binding.

I thought you only needed dynamic binding when the base_class and derived_class have functions/methods that have the same name, and some sort of overriding needs to happen during the compile time to assign them correctly.

Am I totally wrong?

or is there another reason the below code doesn't work?

public class Ticket {

    protected String flightNo;

    public Ticket(){

        this.flightNo = new String();

    }

    public void setTicket(lnode ticket){

        this.flightNo= ticket.getFlightNumber();
    }

    public void displayTicket(){

        System.out.println("Flight number: " + this.flightNo);   
     }
   }

And a derived class extended from the above class

public class originalTicket extends Ticket {
    protected boolean catchAnotherFlight;
    protected boolean baggageTransfer;

    public originalTicket(){

        catchAnotherFlight = false;
        baggageTransfer = false;
    }

    public void setoriginalTicket(lnode ticket){

        setTicket(ticket);

    }
  }

and I am unable to do this:

Ticket object = new originalTicket();
object.setoriginalTicket();//Can't call the originalTicket method.

I have written some programs using upcasting in C++ but I always used dynamic binding. I was just trying to pick up Java and now I am a little startled that I got all the concepts wrong all along. Thanks.


Solution

  • Let me try to explain using more layman terms:

    public abstract class Animal {
        public abstract void move();
    }
    
    public class Cat extends Animal {
        @Override public void move() {
            moveWithLegs();
        }
    
        public void moveWithLegs() {
            // Implementation
        }
    }
    
    public class Fish extends Animal {
        @Override public void move() {
            moveBySwimming();
        }
    
        public void moveBySwimming() {
            // Implementation
        }
    }
    
    Animal animal = new Cat();
    animal.move(); // Okay
    animal.moveWithLegs(); // Not okay
    ((Cat) animal).moveWithLegs(); // Okay
    ((Fish) animal).moveWithLegs(); // ClassCastException exception
    

    The object animal is just an object reference to anything that is an animal. Even though the actual instance type is Cat, the compiler would simply treat it as if it is an Animal.

    Therefore, at compile time, you are only allowed to invoke methods defined by Animal class.

    If you know animal is an instance of Cat, and you would want to call a method in Cat class, you need to cast this. By casting, you are telling the compiler, "Hey I know this thing is a cat, I want you to treat this like a cat."

    Therefore, by casting, you get the access to the methods in Cat class, which also includes all members inherited from Animal class.

    If you are not sure if animal is a Cat or Fish, and yet you still cast and call moveWithLegs(), you would get a ClassCastException at runtime, which means you broke the contract that you agreed on at compile time.