运行时多态性

时间:2011-03-21 09:35:07

标签: java polymorphism

假设我有A级

public class A
{
    public void method()
    {
        //do stuff
    }
}

另一个B类

public class B extends A
{
    public void method()
    {
        //do other stuff
    }
}

现在我有以下声明:

A a = new B();
a.method();

这是运行时多态的一个例子吗?如果是,那么在编译时没有对引用a进行绑定吗?

6 个答案:

答案 0 :(得分:18)

编译器会告诉你这不起作用,因为A和B之间没有任何关系可以让你写A a = new B();

B必须扩展A或两者都必须在其中实现与void method()的公共接口。

您可以通过编译器尝试快速回答这个问题。成为一名实验主义者 - 它比论坛更快。

更新:

它有效,现在B扩展A.你关心的绑定,动态绑定,是在运行时完成的。变量“a”的静态编译时类型是A类;在运行时,它动态绑定到类型B的引用。是的,我认为这是多态的一个例子。

答案 1 :(得分:2)

我正等着把我的答案放到另一篇文章中,进入一个更正确的分类问题。你的问题符合这个概念,我试图详细解释。但是,由于我不知道链接的答案如何,我只需要复制/粘贴完整的脚本....请仔细阅读,你会理解有关你的问题的一切,你问过


假设编译器必须解决这样的调用:*

  

a a =新AA(); //假设AA是A类的一些子类   A-> someFunc(); //我们在

上调用方法“someFunc()”

*。

现在,编译器将有条不紊地执行以下步骤。

1。)首先,编译器知道变量a的声明类型,因此它将检查声明的对象类型a( lets call this, class A for time being )是否有一个名为someFunc的方法()并且它必须是公开的。这个方法既可以在class A中声明,也可以是来自A类基类之一的派生方法,但它与编译器无关,它只是检查它是否存在。访问说明符为public

  • 毋庸置疑,此步骤中的任何错误都会引发编译错误。

2.)其次,一旦该方法被验证为A类的一部分,编译器必须解析对正确方法的调用,因为许多方法可能具有相同的名称(由于函数重载)。解决正确方法的过程称为overloading resolution。编译器通过将被调用方法的签名与作为类的一部分的所有重载方法相匹配来实现此目的。因此,在所有someFunc() s中,只能找到正确的someFunc()(使用被调用的方法匹配签名)并进一步考虑。

3。)现在遇到了困难的部分,很可能会发生someFunc()可能已经在A类(lets call this class AA and needless to say it is some subclass of A)的一个子类中被覆盖了,而变量a(声明为类型A)实际上可能指的是AA类的对象(这在C ++中是允许的,以实现多态性)。现在,如果someFunc()方法被声明为类型virtual,则在基类(即A类)中,someFunc()已被A的子类覆盖(在AA或A之间的类中)和AA),必须由编译器找到someFunc()的正确版本。

  • 现在,想象一下你是编译器,你有这个任务来查找类AA是否有这个方法。显然,AA类将具有此方法,因为它是A的子类,并且A类中的A的公共访问已经在编译器的步骤1中得到验证! 。但另外,如前一段所述,someFunc()可能被类AA(或A和AA之间的任何其他类)覆盖,这是编译器需要捕获的。因此,您(因为,您正在玩​​编译器)可以进行系统检查以找到最重要的(继承树中最低的)重写方法someFunc()从类A开始并在类AA结束。在此搜索中,您将查找与重载分辨率中验证的方法签名相同的方法签名。此方法将是将被调用的方法。

  • 现在,您可能想知道,“这是什么”,这次搜索是什么时候完成的? ......好吧,不是真的。编译器知道每次查找此内容的开销,因此,为每个类类型维护一个名为Virtual Table的数据结构。可以将虚拟表视为从方法签名(可公开访问)到函数指针的映射。此虚拟表由编译器在编译过程中生成,并在程序执行期间保留在内存中。在我们的示例中,A类和AA类都有自己的虚拟表。当编译器必须在类AA中找到someFunc()时(因为变量a指向的实际对象是AA类型),它将简单地通过类AA的虚拟表找到函数指针。这很简单,已经散列到表中并且是一个恒定的时间操作。

此致

AVID

答案 2 :(得分:1)

您提供的代码示例不合法,所以我想答案是它不是一种多态性。

您不能将类分配给不相关类型的变量。我猜你可能打算从A派生出来?

答案 3 :(得分:1)

  

这是运行时多态的一个例子吗?

编辑时:是的,这将是运行时多态,因为实际执行的方法取决于分配给a的内容。

编辑:

如果method()是静态的,那么调用a.method()将始终导致调用A的版本。但是,您只需编写A.method(),如果您不这样做,任何体面的IDE都会警告您(因为在实例上调用静态方法可能会产生误导)。

结束编辑。

编译时多态将是重载机制,即编译器决定是使用someMethod(Number)还是someMethod(Integer),具体取决于它对传递的参数的了解(如果传递Double或者只是Number它将是第一个,如果你传递Integer它将是第二个。

  

如果是,那么在编译时没有对引用a进行绑定吗?

你到底是什么意思?请注意,a的类型将为A,因此您可以在运行时分配扩展A的所有内容。如果指定的值不是A,编译器就会抱怨(你可能会使用强制转换欺骗编译器,但这只是邪恶的,如果这破坏了你的程序,你就会受到警告;)

答案 4 :(得分:0)

应该是

  

公共B级延伸A {
      public void method()
      {
          //做其他事情       }}

答案 5 :(得分:0)

如果正在编译,则会调用B::method()。 (仅当您更改代码以使B类继承A类时)。

在这种情况下,你使用你的对象,就像它只是一个A,但如果在B中覆盖了一些方法,则调用这些方法,而不是A的方法。