在 C++ 中,我必须显式指定“virtual”关键字以使成员函数“可重写”,因为当成员函数可重写时,会涉及创建虚拟表和 vpointers 的开销(因此每个成员函数都隐式不可重写)性能原因)。
当子类提供具有相同名称和签名的单独实现时,它还允许隐藏成员函数(如果不重写)。
C# 中也使用了相同的技术。 我想知道为什么 Java 放弃了这种行为,默认情况下使每个方法都可重写,并提供了在显式使用“final”关键字时禁用重写行为的能力。
In C++, I have to explicitly specify 'virtual' keyword to make a member function 'overridable', as there involves an overhead of creating virtual tables and vpointers, when a member function is made overridable (so every member function is implicitly not overridable for performance reasons).
It also allows a member function to be hidden (if not overridden) when a subclass provides a separate implementation with the same name and signature.
The same technique is used in C# as well. I am wondering why Java waved away from this behavior and made every method overridable by default and provided the ability to disable overriding behavior on explicit use of 'final' keyword.
发布评论
评论(6)
所以 Java 的基本原理可能是这样的:面向对象语言的全部要点是事物可以扩展。 因此,就纯粹的设计而言,将可扩展视为“特殊情况”确实没有什么意义。
请记住,Java 具有在运行时编译的优势。 因此,C++ 编译中的一些性能参数就被排除在外了。 在 C++ 中,如果类可能被覆盖,那么编译器必须采取额外的步骤。 在 Java 中,这并不神秘:在任何给定时刻,JVM 都知道特定方法/类是否已被覆盖,这本质上是什么才是重要的。
请注意,final 关键字本质上是关于程序设计,而不是优化。 JVM 不需要这些信息来查看类/方法是否已被覆盖!
So Java's rationale is probably something like this: the whole point of an object-oriented language is that things can be extended. So in terms of pure design, it really makes little sense to treat extensible as the "special case".
Remember that Java has the luxury of compiling at runtime. So some of the performance arguments in C++ compilation go out the window. In C++, if a class might be overridden, then the compiler has to take extra steps. In Java, there's no mystery about it: at any given moment in time, the JVM knows whether or not a particular method/class has been overridden or not, and that's essentially what counts.
Note that the final keyword is essentially about program design, not optimisation. The JVM doesn't need this information to see whether or not a class/method has been overridden!!
如果问题是要问 java 和 C++/C# 之间更好的方法是什么,那么它已经在另一个线程中以相反的方向进行了讨论,并且网上有许多可用的资源
为什么 C# 默认情况下将方法实现为非虚拟方法?
http://www.artima.com/intv/nonvirtual.html
最近介绍了 @Override 注释及其在新代码中的广泛采用,建议这是“为什么所有 java 方法都是隐式可重写的?”问题的确切答案。 确实是因为设计者犯了一个错误。 (他们已经修好了)
哦! 我将对此投反对票。
If the question is about to ask what is the better approach between java and C++/C# then it was already discussed in opposite direction in another thread, and many resource available on the net
Why C# implements methods as non-virtual by default?
http://www.artima.com/intv/nonvirtual.html
Recent introduction of @Override annotation and its wide adoption in new code, suggest that the exact answer to the question "Why all java methods are implicitly overridable?" is indeed because the designer made a mistake. (And they already fixed it)
Oh ! I'm going to get negative vote for this.
Java 试图向更动态的语言定义靠拢,其中一切都是对象,一切都是虚拟方法。 它还希望避免歧义和难以理解的构造,设计者将其视为 C++ 中的缺陷,因此没有运算符重载,并且在这种情况下,无法在一个类层次结构上拥有两个公共方法签名,根据类型调用不同的方法引用它的变量。
C# 更关心子类的稳定性并确保子类的行为可预测。 C++ 关心的是性能。
三种不同的设计重点,导致不同的选择。
Java tries to move closer to a more dynamic language definition, where everything is an object and everything is a virtual method. It also wants to avoid ambiguity and hard to understand constructs, which it's designers viewed as a flaw in C++, therefore no operator overloading, and in this case no ability to have two public method signatures on one class hierarchy invoking different methods depending on the type of the variable referencing it.
C# is more concerned about the stability of subclasses and making sure that the subclasses behave predictably. C++ is concerned about performance.
Three different design priorities, leading to different choices.
我想说,在 Java 中,与整个 VM 成本相比,虚拟方法的成本较低。 与类似汇编的 C 背景相比,在 C++ 中,这是显着的成本。 作为 C 到 C++ 迁移的结果,没有人会决定默认通过指针调用所有方法。 变化太大了。
I would say that in Java cost of virtual method is low compared to whole VM costs. In C++ it is significant cost, compared to assembly-like C background. Nobody would decide to make all methods called through pointer by default as result of C to C++ migration. It's too big change.
更好的问题可能是“为什么 C# 有非虚拟方法?” 或者至少,为什么它们默认不是虚拟的,并且可以选择将它们标记为非虚拟?
在 C++ 中,有一个想法(正如 Brian 很好地指出的那样):如果你不想要它,你就不用付钱。 问题是,如果你确实想要它,这通常意味着你最终要付出高昂的代价。 在大多数 Java 实现中,它们是为大量虚拟调用而显式设计的; vtable 实现往往速度很快,几乎不比非虚拟调用贵,这意味着非虚拟函数的主要优点丧失了。 此外,JIT 编译器可以在运行时内联虚拟函数。 因此,出于效率原因,实际上几乎没有理由使用非虚拟函数。
因此,这在很大程度上可以归结为最小意外原则。 它告诉我们所有方法的行为方式都相同,不是一半是虚拟的,一半是非虚拟的。 由于我们至少需要一些虚拟方法来实现这种多态性,因此将它们全部设为虚拟是有意义的。 此外,拥有相同签名的两个方法无异于搬起石头砸自己的脚。
多态性还规定对象本身应该能够控制它所做的事情。 它的行为不应该由客户端认为它是 FooParent 还是 FooChild 来确定。
编辑:所以我被要求对我的断言进行调查。 下一段是我的猜测,而不是事实陈述。
所有这一切的一个有趣的副作用是 Java 程序员往往非常频繁地使用接口。 由于虚拟方法优化使接口的成本基本上不存在,因此它们允许您使用 List(例如)而不是 ArrayList,并在稍后的某个日期通过简单的一行更改将其切换为 LinkedList没有额外的处罚。
编辑:我还将支付几个来源。 虽然不是原始来源,但它们确实来自 Sun 解释 HotSpot 的一些工作原理。
内联
虚拟表
The better question might be "Why does C# have non-virtual methods?" Or at the very least, why aren't they virtual by default with the option to flag them as non-virtual?
In C++, there is the idea (as Brian so nicely pointed out) that if you don't want it, you don't pay for it. The problem is that if you do want it, this usually means you end up paying through the nose for it. In most Java implementations, they are designed explicitly for lots of virtual calls; the vtable implementations tend to be fast, scarcely more expensive than non-virtual calls, meaning the primary advantage of non-virtual functions is lost. Furthermore, JIT compilers can inline virtual functions at runtime. As such, for efficiency reasons, there is very little reason actually to use non-virtual functions.
Thus, it largely comes down to the principle of least surprise. It tells us that all methods to behave the same way, not half of them being virtual and half of them being non-virtual. Since we need to have at least some virtual methods to achieve this polymorphism thing, it makes sense to have them all be virtual. Furthermore, having two methods with the same signature is just asking to shoot yourself in the foot.
Polymorphism also dictates that the object itself should have control over what it does. It's behavior should not be determinate on whether the client thinks it's a FooParent or a FooChild.
EDIT: So I'm being called on my assertions. This next paragraph is conjecture on my part, not a statement of fact.
An interesting side effect of all this is that Java programmers tend to use interfaces very heavily. Since the virtual method optimizations make the cost of interfaces essentially non-existent, they allow you to use a List (for example) instead of an ArrayList, and switch it out for a LinkedList at some later date with a simple one-line change and no additional penalty.
EDIT: I'll also pony up a couple sources. While not the original sources, they do come from Sun explaining some of the workings on HotSpot.
Inlining
VTable
取自此处 (#34)
也许有点圆形。
Taken from here (#34)
A bit circular, perhaps.