重载是编译时多态.真?

Jom*_*oos 24 java polymorphism overriding overloading method-dispatch

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

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class FancyGreeter extends Greeter {
    public void greetMe() {
        System.out.println("***********");
        System.out.println("*  Hello  *");
        System.out.println("***********");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Greeter greeter = GreeterFactory.getRandomGreeter();
greeter.greetMe();
Run Code Online (Sandbox Code Playgroud)

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

在Java中,所有非静态方法默认为"虚函数".只有标记为关键字final的方法(不能被覆盖)以及未被继承的私有方法才是非虚拟方法.

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

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

vau*_*oid 14

每个"迎宾"类有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!");
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此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!"
Run Code Online (Sandbox Code Playgroud)

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

最后一个调用是最有趣的:即使strAsObj的运行时类型是String,它也被Object编译为编译器看到它的方式.因此,编译器可以为该调用找到的最接近的匹配是该void greetMe(Object)方法.


for*_*ran 12

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

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

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

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

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


Azo*_*ous 10

什么是多态?

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

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

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

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

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

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

It's compile-time because before execution of program compiler decides the flow of program i.e 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 i.e. only during run-time it can be decided that which form will be used.

我希望它清除一点.