重载是编译时多态。 真?

我知道覆盖和重载之间的语法差异。 我也知道覆盖是运行时多态,重载是编译时多态。 但我的问题是:“重载是否真的是编译时多态?方法调用是否真的在编译时求解?”。 为了澄清我的观点,让我们考虑一个示例类。

public class Greeter { public void greetMe() { System.out.println("Hello"); } public void greetMe(String name) { System.out.println("Hello " + name); } public void wishLuck() { System.out.println("Good Luck"); } } 

由于所有方法greetMe(), greetMe(String name), wishLuck()都是公共的,它们都可以被覆盖(包括重载的),对吧? 例如,

 public class FancyGreeter extends Greeter { public void greetMe() { System.out.println("***********"); System.out.println("* Hello *"); System.out.println("***********"); } } 

现在,请考虑以下代码段:

 Greeter greeter = GreeterFactory.getRandomGreeter(); greeter.greetMe(); 

getRandomGreeter()方法返回一个随机的Greeter对象。 它可以返回Greeter的对象或其任何子类,如FancyGreeterGraphicalGreeter或任何其他子类。 getRandomGreeter()将使用new或动态加载类文件创建对象,并使用reflection创建对象(我认为可以使用reflection)或任何其他可能的方式。 Greeter所有这些方法可能在子类中被覆盖,也可能不被覆盖。 因此编译器无法知道是否覆盖了特定方法(是否重载)。 对? 此外,维基百科在虚拟function上说:

在Java中,默认情况下所有非静态方法都是“虚函数”。 只有标记为关键字final的方法(不能被覆盖)以及未被inheritance的私有方法才是非虚拟方法。

因为,使用动态方法分派在运行时解析虚函数,并且由于所有非私有,非最终方法都是虚拟的(无论是否过载),因此必须在运行时解析它们。 对?

然后,如何在编译时解决重载? 或者,有什么我误解了,或者我错过了什么?

如果这是你要求的,仍然可以覆盖重载的方法。

重载方法就像不同的系列,即使它们具有相同的名称。 编译器静态选择给定签名的一个系列,然后在运行时将其分派给类层次结构中最具体的方法。

也就是说,方法调度分两步执行:

  • 第一个是在编译时使用静态信息完成的,编译器将发出一个签名call ,该签名最适合当前方法参数,该方法参数在调用该方法的对象的声明类型中的重载方法列表中。
  • 第二步是在运行时执行,给定应该调用的方法签名(前一步,记得吗?),JVM会将它调度到实际类型的接收器对象中最具体的重写版本。

如果方法参数类型根本不是协变的,那么重载相当于在编译时损坏方法名称; 因为它们实际上是不同的方法,JVM永远不会根据接收器的类型交替地分配它们。

每个’Greeter’类都有3个虚拟方法: void greetMe()void greetMe(String)void wishLuck()

当你调用greeter.greetMe() ,编译器可以确定应该从方法签名中调用三个虚拟方法中的哪一个 – 即。 void greetMe()一,因为它不接受任何参数。 调用void greetMe()方法的具体实现取决于greeter实例的类型,并在运行时解析。

在您的示例中,编译器计算调用哪个方法很简单,因为方法签名完全不同。 显示“编译时多态”概念的稍微好一些示例可能如下:

 class Greeter { public void greetMe(Object obj) { System.out.println("Hello Object!"); } public void greetMe(String str) { System.out.println("Hello String!"); } } 

使用此greeter类将得到以下结果:

 Object obj = new Object(); String str = "blah"; Object strAsObj = str; greeter.greetMe(obj); // prints "Hello Object!" greeter.greetMe(str); // prints "Hello String!" greeter.greetMe(strAsObj); // prints "Hello Object!" 

编译器将使用编译时类型选择具有最特定匹配的方法,这就是第二个示例的工作原理并调用void greetMe(String)方法。

最后一个调用是最有趣的:尽管strAsObj的运行时类型是String ,但它已被转换为Object因此编译器会看到它。 因此,编译器可以为该调用找到的最接近的匹配是void greetMe(Object)方法。

什么是多态?

加。 对我来说如果一个实体可以用多种forms表示,那么该实体就会表现出多态性。

现在,让我们将这个定义应用于Java构造:

1)运算符重载是编译时多态。

例如, +运算符可用于添加两个数字OR以连接两个字符串。 它是多态性的一个例子,严格地说是编译时多态性。

2)方法重载是编译时多态。

例如,具有相同名称的方法可以具有多个实现。 它也是一个编译时多态。

It's compile-time because before execution of program compiler decides the flow of program ie which form will be used during run-time.

3)方法覆盖是运行时多态。

例如,具有相同签名的方法可以具有多个实现。 这是一个运行时多态。

4)基类使用代替派生类是运行时多态。

例如, interface引用可以指向它的任何实现者。

It's run-time because the flow of program can't be known before execution ie only during run-time it can be decided that which form will be used.

我希望它有点清楚。

在这方面重载意味着函数的类型在编译时静态确定,而不是动态调度。

幕后真正发生的是,对于名为“foo”的方法,类型为“A”和“B”,创建了两个方法(“foo_A”和“foo_B”)。 在编译时( foo((A) object)foo((B) object) foo_A要调用foo_B导致调用foo_Bfoo_B )。 所以在某种程度上这编译时多态,尽管真正的方法(即要在类层次结构中采用哪种实现)是在运行时确定的。