confusion about upcasting vs dynamic binding

北慕城南 提交于 2021-01-05 07:13:57

问题


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.


回答1:


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.




回答2:


My original understanding was that when you create a base_class pointer 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.

That's incorrect. obj is a reference variable of type base_class, so base_class defines the interface you have access to via obj (the fields and methods you can use with that reference). So you can use obj.setTicket and obj.displayTicket, but not obj.originalTicket because originalTicket isn't part of the base_class interface. (If you know the object the variable refers to is actually a derived_class object, you could use use casting to change the interface you have to the object: ((derived_class)obj).originalTicket.)

What you may be thinking of is that although the interface you have to the object through obj is defined by base_class, the implementation is from derived_class. So to take a simpler example:

class Base {
    public void a() {
        System.out.println("base a");
    }
}
class Derived extends Base {
    @Override
    public void a() {
        // (Often you'd use `super.a()` here somewhere, but I'm not going to
        // in this specific example)
        System.out.println("derived a");
    }
    public void b() {
        System.out.println("derived b");
    }
}

Then:

Base obj = new Derived();
obj.a(); // "derived a"
obj.b(); // Compilation error

Note that although our interface to the object is of type Base, when we call obj.a(), we get Derived's implementation of it — because the object is actually a Derived instance.




回答3:


Let's consider C++ as a starting point, as it is the language you are coming from:

class Base
{
    void f() { };
};

class Derived : public Base
{
    void g() { };
};

Base* b = new Derived();
b->g();

Would it work?

You only can call methods (Java)/functions (C++) that are known in the base class. You can override them by making them virtual (C++; Java: all non-static methods are implicitly virtual!):

class Base
{
    virtual void f() { };
};

class Derived : public Base
{
    virtual void f() override { };
};

Base* b = new Derived();
b->f(); // calls Derived's variant of!

Getting back to the first example: You need a downcast to call the new function:

Base* b = new Derived();
static_cast<Derived*>(b)->g(); // if you know for sure that the type is correct
auto d = dynamic_cast<Derived*>(b);
if(d) d->g(); // if you are not sure about the actual type

Exactly the same in Java, just that you do not have explicit pointers, but implicit references...

Base b = new Derived();
((Derived)b).g();



回答4:


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

You can only call the declared interface's methods in Java. In your case only methods of base_class.

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.

Method calls in inheritance are always evaluated in runtime with Java. So, in your example this would mean, that you have to declare your classes in the following way:

public class Ticket {
    ...
    public void setTicket() {
        System.out.println("I am the superclass");
    }
    ...
}

And this is your subclass

public class OriginalTicket extends Ticket {
    ...
    @Override
    public void setTicket(){
        System.out.println("I am the subclass");
        super.setTicket();
    }
    ...
  }

You have to call setTicket() this way.

Ticket object = new OriginalTicket();
object.setTicket();

At runtime it doesn't matter if you declare your variable's interface as Ticket or OriginalTicket. Output will always be:

"I am the subclass"
"I am the superclass"

Calling setTicket() through the OriginalTicket interface won't make any different during runtime evaluation.

OriginalTicket object = new OriginalTicket();
object.setTicket();

Output will the be the same:

"I am the subclass"
"I am the superclass"

If you omit the super.setTicket() from the implementation, Ticket.setTicket() method is not called, and output will be:

"I am the subclass"



回答5:


Mostly when you do polymorphism, you're using abstract class or Interface as Base.

As we know we're doing UPCASTING at runtime-polymorphism, and this is achieved by method-overriding. So you can only access override methods in a base class. If you want to access the derived class completely you need to tell the compiler explicitly by casting it.



来源:https://stackoverflow.com/questions/49251434/confusion-about-upcasting-vs-dynamic-binding

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!