代码之家  ›  专栏  ›  技术社区  ›  Luc Aux

关于上传与动态绑定的混淆

  •  1
  • Luc Aux  · 技术社区  · 7 年前

    所以我在理解动态绑定和升级时有些困惑。

    我最初的理解是 base_class 引用并将其分配给派生类对象,如:

    base_class obj = new derived_class();
    

    现在可以调用 derived_class 具有 obj.method_of_derived class() ,无动态绑定。

    我以为您只需要在 base\u类 派生的\u类 具有具有相同名称的函数/方法,并且需要在编译时进行某种重写才能正确分配它们。

    我完全错了吗?

    或者下面的代码不起作用还有其他原因吗?

    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);   
         }
       }
    

    以及从上述类扩展而来的派生类

    public class originalTicket extends Ticket {
        protected boolean catchAnotherFlight;
        protected boolean baggageTransfer;
    
        public originalTicket(){
    
            catchAnotherFlight = false;
            baggageTransfer = false;
        }
    
        public void setoriginalTicket(lnode ticket){
    
            setTicket(ticket);
    
        }
      }
    

    我无法做到这一点:

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

    我在C++中编写了一些使用向上转换的程序,但我总是使用动态绑定。我只是想学习Java,现在我有点吃惊,因为我一直把所有的概念都弄错了。谢谢

    5 回复  |  直到 7 年前
        1
  •  3
  •   Jai    7 年前

    让我试着用更多的外行术语来解释:

    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
    

    对象 animal 只是一个对象引用 一种动物。即使实际实例类型为 Cat ,编译器会简单地将其视为 Animal

    因此,在编译时,只允许调用 动物

    如果你知道 动物 是的实例 ,并且您希望在中调用方法 同学们,你们需要投下这个。通过施法,你告诉编译器,“嘿,我知道这东西是一只猫,我想让你把它当作一只猫来对待。”

    因此,通过强制转换,您可以访问 类,该类还包括从 动物

    如果您不确定 动物 是一个 Fish ,但你仍然投下并呼叫 moveWithLegs() ,您将获得 ClassCastException 在运行时,这意味着您违反了在编译时商定的约定。

        2
  •  3
  •   Community CDub    4 年前

    我最初的理解是,当创建base\u类指针并将其分配给派生类对象时,如:

    base_class obj = new derived_class();
    

    现在可以使用obj调用派生的\u类的方法/成员函数。\u派生类()的方法\u,没有动态绑定。

    这是错误的。 obj 是类型的引用变量 base_class 所以 base\u类 定义您可以通过访问的接口 obj公司 (可用于该引用的字段和方法)。所以你可以使用 obj.setTicket obj.displayTicket ,但不是 obj.originalTicket 因为 originalTicket 不是 base\u类 界面(如果您知道变量引用的对象实际上是 derived_class 对象,您可以使用强制转换来更改与对象的接口: ((derived_class)obj).originalTicket .)

    你可能会想,虽然 界面 你必须通过 obj公司 定义为 base\u类 这个 实施 来自 派生的\u类 。举个简单的例子:

    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");
        }
    }
    

    然后:

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

    注意,虽然我们的对象接口是 Base ,当我们打电话时 obj.a() ,我们得到 Derived 的实现,因为对象实际上是 派生的 例子

        3
  •  2
  •   Aconcagua    7 年前

    让我们把C++作为一个起点,因为它是您所使用的语言:

    class Base
    {
        void f() { };
    };
    
    class Derived : public Base
    {
        void g() { };
    };
    
    Base* b = new Derived();
    b->g();
    

    这样行吗?

    您只能调用基类中已知的方法(Java)/函数(C++)。您可以通过使其虚拟化来覆盖它们(C++;Java:所有非静态方法都是隐式虚拟的!):

    class Base
    {
        virtual void f() { };
    };
    
    class Derived : public Base
    {
        virtual void f() override { };
    };
    
    Base* b = new Derived();
    b->f(); // calls Derived's variant of!
    

    回到第一个例子:你需要 向下 cast调用新函数:

    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
    

    在Java中完全一样,只是没有显式指针,而是隐式引用。。。

    Base b = new Derived();
    ((Derived)b).g();
    
        4
  •  2
  •   Mike Williamson    6 年前

    现在可以使用obj调用派生的\u类的方法/成员函数。\u派生的\u类()的方法\u,没有动态绑定。

    您只能在Java中调用已声明接口的方法。在您的情况下,只有 base_class

    我认为您只需要在基类和 派生类具有同名的函数/方法,以及 在要分配的编译时需要进行某种重写 正确地执行这些操作。

    继承中的方法调用总是在Java运行时进行评估。因此,在您的示例中,这意味着您必须以以下方式声明类:

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

    这是你的子类

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

    你必须打电话 setTicket() 这边

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

    在运行时,将变量的接口声明为 Ticket OriginalTicket 。输出将始终为:

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

    使命感 setTicket() 通过 原始小盒 在运行时评估期间,接口不会有任何不同。

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

    输出将相同:

    “我是子类”
    “我是超类”
    

    如果省略 super.setTicket() 从实施来看, Ticket.setTicket() 方法,输出将为:

    "I am the subclass"
    
        5
  •  0
  •   Mudasir H    4 年前

    大多数情况下,当您执行多态性时,您使用的是抽象类或接口作为基础。

    正如我们所知,我们正在做的 向上投射 在运行时多态性,这是通过方法重写实现的。因此,只能访问基类中的重写方法。 如果要完全访问派生类,需要通过强制转换来显式地告诉编译器。