5

我刚刚了解到,在 Java 中,覆盖和隐藏之间存在区别(静态方法被隐藏而不是覆盖),这意味着 Java 使用早期绑定和后期绑定。

是否有类似于方法隐藏的东西,或者它只是有方法覆盖?

4

1 回答 1

10

Java 有三种不同的“方法”:实例方法、静态方法和构造函数。Ruby 只有一个:实例方法。

在 Java 中,静态方法的行为必须与实例方法不同,因为类不是对象。他们没有类,因此没有超类,所以没有什么可以覆盖的。在 Ruby 中,类就像任何其他对象一样是对象,它们有一个类,它可以有一个超类,因此子类可以覆盖超类的方法。

注意:您可能听说过 Ruby 中的类方法或单例方法。这是一个谎言。好吧,好吧,不是谎言。这是我们使用的一种方便的简写,因为“类方法”比“类对象的单例类的常规实例方法”更容易发音……但这正是它的本质。没有类方法。

在 Ruby 中,每个对象都可以有自己的方法。这些被称为“单例方法”。类与任何其他对象一样是对象,因此它们也可以具有单例方法。当单例方法所属的对象是一个类时,我们称该方法为类方法。但这就是我们所说的,类方法和单例方法没有区别。

实际上,在 Ruby 中,每个对象都有一个单例类。单例类与对象是 1:1 的关系:对象只有一个单例类,每个单例类只有一个实例,即它的对象。那么,当我上面说对象可以有方法并且这些方法称为单例方法时?嗯,那也是一个谎言。单例方法实际上只是标准实例方法,它们恰好在对象的单例类中定义,因此只能在该对象上调用(因为该对象是其单例类的唯一实例)。

所以,当一个方法在单例类中定义时,我们称它为单例方法,当单例类属于一个类时,我们称它为类方法,但都是实例方法。(顺便说一句:模块的工作方式相同。在这种情况下,它们被称为模块方法或有时称为“模块函数”。)

对象的class指针总是指向它的单例类。那么对象的实际superclass类就是单例类的,即单例类的superclass指针指向对象的实际类。(除非有mixin,它们变成了它们混入的类的超类,所以如果你把一个模块混入一个单例类,这个模块就变成了单例类的超类,而旧的超类变成了模块的超类,或者而是它包含代理类。)

这意味着方法查找(OO 语言中最常执行的操作)变得非常简单且非常快速:获取对象,获取其class指针,查看方法是否存在,获取超类指针,查看方法就在那里,抓住超类指针……直到找到方法。

这确实意味着反射变得更加复杂,但反射不是性能关键操作。例如,如果你向一个对象询问它的​​类,你不能简单地返回class指针,因为它总是它的单例类,因此信息量不大。你必须得到超类,以及超类的超类等等,直到你最终得到一个不是单例类或包含代理类的类。

但是方法查找本身非常简单,并且super总是按照您的期望进行。

特别是,当你创建一个新类时,超类的单例类变成了子类的单例类的超类,这样“类方法”就如你所愿地被继承了。

所以,回顾一下:虽然 Java 有三种不同的“方法”,它们具有不同的继承行为(实例方法被继承,静态方法不被继承,构造函数被继承但有这个super调用限制),而 Ruby 只有一种。但是,它具有三种不同的类:常规类、单例类和包含代理类(在将模块混入类时创建为 mixins 的代理)。后两者在 YARV(最广泛使用的 Ruby 实现)中也称为“虚拟类”。

最后一件事:还有所谓的“全局方法”,有时称为“全局过程”或“全局函数”。同样,正如您可能已经猜到的那样,这些不存在。当您在任何类之外定义方法时,它会隐式成为私有实例方法,Object因此可用于每个对象。

[我在这里忽略了两件事:BasicObjectprepend. 这些使事情有些复杂化,尤其是后者。但主要的心智模型仍然存在。]

于 2015-09-02T16:57:34.400 回答