Java继承与多态 [英] Java inheritance vs polymorphism

查看:100
本文介绍了Java继承与多态的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

继承和多态是否构成IS-A关系?并且在运行时发生继承和重写多态是真的,而在编译时发生重载多态吗?我问这个的原因是因为很多论坛似乎给出了相互矛盾且经常令人困惑的答案。

Do both inheritance and polymorphism constitute an IS-A relationship? And is it true that inheritance and "overriding" polymorphism happen in runtime while "overloading" polymorphism occurs in compile time? The reason I ask this is because a lot of forums seem to give conflicting and often confusing answers.

谢谢!

推荐答案

关于你的问题的第一部分,我认为维基百科提供了一个很好的定义:

For your first part of the question I think Wikipedia provides a good definition:


在面向对象的编程中,子类型多态或包含
多态是一个概念在类型理论中,名称可以表示许多不同类的
实例,只要它们与
相关联的一些常见超类。包含多态性通常是通过子类型支持
,即不同类型的对象是
完全可替代另一种类型的对象(它们的基本
类型),因此可以通过公共处理接口。
或者,包含多态性可以通过类型
强制来实现,也称为类型转换。

In object-oriented programming, subtype polymorphism or inclusion polymorphism is a concept in type theory wherein a name may denote instances of many different classes as long as they are related by some common super class. Inclusion polymorphism is generally supported through subtyping, i.e., objects of different types are entirely substitutable for objects of another type (their base type(s)) and thus can be handled via a common interface. Alternatively, inclusion polymorphism may be achieved through type coercion, also known as type casting.

另一个维基百科artile称为面向对象编程中的多态性似乎很好地回答了你的问题。本文中的第二个参考文献关于理解类型,数据抽象和多态性也涵盖了这一点非常详细。

Another Wikipedia artile called Polymorphism in object-oriented programming seems to answer your questions very well. The second reference in this article called On Understanding Types, Data Abstraction, and Polymorphism also covers this matters in great detail.

Java中的这种子类型功能是通过继承类和接口来实现的。尽管Java的子类型特征在继承方面可能并不明显。以协方差和与泛型的逆变为例。此外,数组是Serializable和Cloneable,尽管在类型层次结构中的任何位置都不明显。也可以说通过原始扩展转换,Java中的数字类型也是多态的。并且运算符根据其操作数进行多形态处理。

This subtyping feature in Java is achieved, among other means, through inheritance of classes and interfaces. Although the subtyping features of Java may not be evident in terms of inheritance all the time. Take for example the cases of covariance and contravariance with generics. Also, arrays are Serializable and Cloneable although this is not evident anywhere in the type hierarchy. It can also be said that through primitive widening conversion, also numeric types in Java are polymorphic. And operator behave polimorphically depending on their operands.

无论如何,继承在实现某些多态中起着重要作用。

At any rate, inheritance plays an important role in the implementation of some of this polymorphism.

超载与覆盖

您问题的第二部分似乎是关于选择给定方法的实现。显然,如果一个类重写了一个方法并且你创建了该类的实例,你希望调用该方法的重写版本,即使你是通过父类的引用访问该对象。

Your second part of the question seems to be about choosing the implementation of a given method. Evidently, if a class overrides a method and you create an instance of that class you want the overriden version of method to be invoked, even if you are accessing the object through a reference of the parent class.

正如你所指出的,方法的正确实现的选择是在运行时完成的,现在要调用的方法的签名是在编译时决定的。由于重载是关于具有相同名称和不同签名的不同方法,因此可以说在编译时发生重写方法选择。

The selection of the right implementation of method is done at runtime as you well pointed out, now the signature of the method to be invoked is decided at compile time. Since overloading is about different methods with the same name and different signature, that is why it is said that overriding method selection happens at compile time.

覆盖方法选择在编译时

Java第15.12节中的语言规范(JLS)方法调用表达式详细解释了编译器在选择正确的调用方法时遵循的过程。

The Java Language Specification (JLS) in section 15.12 Method Invocation Expressions explains in detail the process that the compiler follows to choose the right method to invoke.

在那里,你会注意到这是编译时任务。 JLS在第15.12.2小节中说:

There, you will notice that this is a compile-time task. The JLS says in subsection 15.12.2:


此步骤使用方法的名称用于查找可访问和适用的方法的参数表达式的类型

可能有多个此类方法,在这种情况下,选择最具体的方法。

This step uses the name of the method and the types of the argument expressions to locate methods that are both accessible and applicable There may be more than one such method, in which case the most specific one is chosen.

要验证此编译时的性质,您可以进行以下测试。

To verify the compile-time nature of this, you can do the following test.

声明一个这样的类并编译它。

Declare a class like this and compile it.

public class ChooseMethod {
   public void doSomething(Number n){
    System.out.println("Number");
   }
}

声明第二个类调用第一个方法一个并编译它。

Declare a second class that invokes a method of the first one and compile it.

public class MethodChooser {
   public static void main(String[] args) {
    ChooseMethod m = new ChooseMethod();
    m.doSomething(10);
   }
}

如果你调用main,输出显示数字

If you invoke the main, the output says Number.

现在,向<$ c $添加第二个更具体的重载方法c> ChooseMethod class,并重新编译它(但不要重新编译其他类)。

Now, add a second more specific overloaded method to the ChooseMethod class, and recompile it (but do not recompile the other class).

public void doSomething(Integer i) {
 System.out.println("Integer");
}

如果再次运行main,输出仍为数字

If you run the main again, the output is still Number.

基本上,因为它是在编译时决定的。如果重新编译 MethodChooser 类(带有main的类),并再次运行程序,输出将为 Integer

Basically, because it was decided at compile time. If you recompile the MethodChooser class (the one with the main), and run the program again, the output will be Integer.

因此,如果要强制选择其中一个重载方法,参数类型必须与编译时参数的类型相对应,而不仅仅是在运行时。

As such, if you want to force the selection of one of the overloaded methods, the type of the arguments must correspond with the type of the parameters at compile time, and not only at run time.

在运行时覆盖方法选择

同样,方法的签名在编译时决定,但实际的实现是在运行时决定的。

Again, the signature of the method is decided at compile time, but the actual implementation is decided at runtime.

声明一个这样的类并编译它。

Declare a class like this and compile it.

public class ChooseMethodA {
   public void doSomething(Number n){
    System.out.println("Number A");
   }
}

然后声明第二个扩展类并编译:

Then declare a second extending class and compile:

public class ChooseMethodB extends ChooseMethodA {  }

在MethodChooser类中你可以:

And in the MethodChooser class you do:

public class MethodChooser {
    public static void main(String[] args) {
        ChooseMethodA m = new ChooseMethodB();
        m.doSomething(10);
    }
}

如果你运行它,你得到输出 Number A ,这没关系,因为该方法尚未在 ChooseMethodB 中被覆盖,因此被调用的实现是 ChooseMethodA

And if you run it you get the output Number A, and this is Ok, because the method has not been overriden in ChooseMethodB and therefore the implementation being invoked is that of ChooseMethodA.

现在,在 MethodChooserB :

public void doSomething(Number n){
    System.out.println("Number B");
}

重新编译这个,然后再次运行main方法。

And recompile just this one, and run the main method again.

现在,您获得输出数字B

因此,实现是在运行时选择的,不需要重新编译 MethodChooser 类。

As such, the implementation was chosen at runtime, and not recompilation of the MethodChooser class was required.

这篇关于Java继承与多态的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆