Java 重载是编译时多态性。真正地
我确实知道重写和重载之间的语法区别。我还知道重写是运行时多态性,重载是编译时多态性。但我的问题是:“重载真的是编译时多态性吗?方法调用真的是在编译时解决的吗?”。为了阐明我的观点,让我们考虑一个示例类。Java 重载是编译时多态性。真正地,java,polymorphism,overloading,overriding,method-dispatch,Java,Polymorphism,Overloading,Overriding,Method Dispatch,我确实知道重写和重载之间的语法区别。我还知道重写是运行时多态性,重载是编译时多态性。但我的问题是:“重载真的是编译时多态性吗?方法调用真的是在编译时解决的吗?”。为了阐明我的观点,让我们考虑一个示例类。 public class Greeter { public void greetMe() { System.out.println("Hello"); } public void greetMe(String name) { System
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(字符串名)、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
的对象,或其任何子类,如FancyGreeter
或GraphicalGreeter
或任何其他子类。getRandomGreeter()
将使用new
创建对象,或者动态加载类文件并使用反射(我认为反射是可能的)或任何其他可能的方式创建对象。Greeter
的所有这些方法都可以在子类中重写,也可以不重写。因此,编译器无法知道某个特定方法(重载或未重载)是否被重写。对吗?此外,维基百科还说:
在Java中,默认情况下,所有非静态方法都是“虚拟函数”。
仅使用关键字final标记的方法,该关键字不能被重写,
与非继承的私有方法一起,它们是非虚拟的
由于虚拟函数是在运行时使用动态方法分派解析的,而且由于所有非私有、非最终方法都是虚拟的(无论是否重载),因此必须在运行时解析它们。对吧?
那么,如何在编译时解决重载问题呢?或者,有什么我误解了,或者我遗漏了吗?重载方法仍然可以被重写,如果这是您要问的 重载方法类似于不同的族,即使它们共享相同的名称。编译器静态地选择一个给定签名的族,然后在运行时将其分派给类层次结构中最特定的方法 也就是说,方法调度分两步执行:
- 第一个是在编译时完成的,在静态信息可用的情况下,编译器将发出一个
,用于签名,该签名在调用方法所依据的对象的声明类型的重载方法列表中与当前方法参数最匹配调用
- 第二步在运行时执行,给定应该调用的方法签名(上一步,记得吗?),JVM将把它分派到实际类型的接收方对象中最具体的重写版本
如果方法参数类型根本不是协变的,则重载相当于在编译时损坏方法名称;因为它们实际上是不同的方法,JVM永远不会根据接收器的类型互换地分派它们。在这方面,重载意味着函数的类型是在编译时静态确定的,而不是动态分派 幕后真正发生的事情是,对于名为“foo”且类型为“a”和“B”的方法,创建了两个方法(“foo_a”和“foo_B”)。在编译时确定调用哪一个(
foo((A)对象)
或foo((B)对象)
导致调用foo\u A
或foo\u B
)。因此,在某种程度上,这就是编译时多态性,尽管真正的方法(即,在类层次结构中采用哪个实现)是在运行时确定的。什么是多态性
根据我的说法:如果一个实体可以用一种以上的形式表示,则该实体被称为具有多态性。
现在,让我们将此定义应用于Java构造:
1)运算符重载是编译时多态性。
例如,+
运算符可用于添加两个数字或连接两个字符串。这是多态性的一个例子,严格来说就是编译时多态性
2)方法重载是编译时多态性。
例如,具有相同名称的方法可以有多个实现。它也是编译时多态性
这是编译时,因为在执行程序之前,编译器决定程序的流程,即在运行时使用哪种形式。
3)方法重写是运行时多态性。
例如,具有相同签名的方法可以有多个实现。这是一个运行时多态性
4)基类代替派生类的用法是运行时多态性。
例如,接口
引用可以指向它的任何实现者
它是运行时的,因为在执行之前无法知道程序流,也就是说,只有在运行时才能决定使用哪种形式。
我希望它能澄清一点。每个“Greeter”类都有3个虚拟方法:
void greetMe()
,void greetMe(String)
,和void wishLuck()
调用greeter.greetMe()
时,编译器可以计算出应从方法签名调用三个虚拟方法中的哪一个,即void greetMe()
one,因为它不接受任何参数。调用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!");
}
}
使用这个迎宾类将
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!"