【问题标题】:Why are methods virtual by default in Java, but non-virtual by default in C#? [closed]为什么Java中的方法默认是虚拟的,而C#中的方法默认是非虚拟的? [关闭]
【发布时间】:2010-11-01 16:12:12
【问题描述】:

在Java中,方法默认是虚拟的; C# 正好相反。

哪个更好?每种方法的优缺点是什么?

【问题讨论】:

  • 我认为它与运行时有关..我也在等待答案+1
  • 什么是“虚拟”?我用java编写代码,我以前从未听说过。
  • 对于 Java 程序员:en.wikipedia.org/wiki/Virtual_function - “virtual”是 C++ 中允许子类覆盖(实例)方法的限定符。 Java 中的非静态方法始终是可覆盖的(即默认情况下为“虚拟”),但在 C++ 中,程序员必须显式声明一个方法为虚拟方法,以便子类能够覆盖它。另请参阅虚拟方法表 (en.wikipedia.org/wiki/Virtual_table)。
  • @Bert F:好的,所以我将其理解为“非最终”。这将是正确的 java 方式说它不是?

标签: c# java oop


【解决方案1】:

默认情况下虚拟比非虚拟好得多有两个主要原因。

  1. OOP 有用性的主要原则是 Liskov 替换原则多态性后期绑定。我一直使用策略模式,为此我希望我的方法是虚拟的。如果你是开放/封闭原则的粉丝,你应该更喜欢 Java 哲学。您应该能够在不更改源代码的情况下更改行为。您可以使用依赖注入和虚拟方法来做到这一点。
  2. 如果您调用一个非虚拟方法,那么您想从您的代码中知道您正在调用哪个类方法。 .net 的缺陷是您无法从代码中知道这一点。
  3. virtual-only 方法的另一个好处是更容易测试代码,因为您可以制作自己(或第 3 方)类的 Mocks。在 Java 中使用 Mockito 进行测试非常简单。

示例

在 Java 中,如果您将 ClassB 定义为

public class ClassB extends ClassA {
    @Override 
    public void run() {
    }
}

和对象

ClassA obj=new ClassB();

如果您调用 obj.run(),您如何知道该代码是否遵循多态打开/关闭原则的规则,或者它将编写与 ClassA 相关的方法?在 Java 中,您将知道总是存在多态性。制作 mock 更容易,扩展类更容易,遵循 Liskov 替换原则。

另一方面,静态方法绑定到一个类,因此如果您想调用与 ClassA 相关的方法,您可以像这样定义该方法:

public static run(ClassA obj)

你可以调用它

ClassB obj=new ClassB();
ClassA.run(obj);

从代码中你会知道你调用的方法是在 ClassA 中定义的,而不是在 ClassB 中。同样在这种情况下,您将不会有虚拟方法的开销。 (注意 JIT 在很多情况下也会减少虚方法的开销)。

对于 C#,如果使方法成为非虚拟方法的原因是能够在子类中定义它但不涉及多态性,那么您可能无缘无故地进行子类化。

如果是为了设计,我建议尽可能密封类(java 中的 final)而不是单独的方法。

Jon Skeet 说,在 C# 中,默认情况下应该密封类,因为默认情况下方法也是非虚拟的。 Joshua Bloch 说你应该设计继承或禁止它(使类最终)。 C# 设计人员选择了一种不一致的混合方法。

【讨论】:

  • 反对者,请解释一下?您可能投反对票是对的,但我想了解您的立场并可能同意这一点。谢谢:)
【解决方案2】:

一如既往,有利也有弊。 C# 使 AOP 更难以实现,但正如其他人所提到的,事后看来可能是 20/20。这一切都归结为您是否相信类必须为扩展而设计,或者只是为了不可预见的行为修改而保持开放。在设计语言时,这是一个很难回答的问题。我认为行业经验倾向于 C# 采用的更保守的方法。

【讨论】:

    【解决方案3】:

    这是时间的扰动。 C++ 默认使用 virtual 关键字和 final。

    Java 遵循 C++ 并试图取其精华并改进其缺点。过度使用继承的危险还没有暴露出来,所以 Java 选择使用 final 关键字和 virtual 作为默认值。

    C# 遵循 Java 并具有后见之明的好处。 Anders 在观察了 Java 的经验后选择回归 C++ 约定。

    【讨论】:

      【解决方案4】:

      .Net 强制程序员定义哪些函数可以被覆盖,而 Java 函数默认情况下可以被覆盖,除非使用 final 关键字。

      如果您是Open/Close Principle 的坚定拥护者,您可能倾向于支持Java 方式。最好允许扩展类并覆盖方法,这样基本功能/代码不会受到影响。出于这个原因,我支持/更喜欢 Java 方式。如果我从不同的角度来看这个问题,我的看法可能正好相反。

      【讨论】:

      • 好吧,我不确定 C# 是否遵循 OCP,因为您可以添加包装原始类的公共 API 的新功能。甚至,我认为 C# 比 Java 更遵循 OCP,因为默认情况下使用 Java 的虚拟方法,您没有扩展但实际上能够更改原始行为,恕我直言实际上违反了 OCP。这只是我的意见:)
      【解决方案5】:

      Java 的方式更简单,C# 的方式默认更细粒度、更安全、更高效。哪个更好在啤酒持有者的眼中。

      【讨论】:

      • 我的观点恰恰相反。 Java 更复杂,因为它有隐藏的成本。虚拟方法在速度和设计成本上都不是免费的。
      • 那么你认为汇编语言是最不复杂的语言吗?
      • 使用*(最少傻字符)
      • @Gerald,这是一个错误的类比。您正在将高级语言与汇编进行比较。我认为虚拟方法更难维护,因为您必须对对象层次结构有更深入的了解才能了解方法如何适合系统。因此,我认为 C# 通常比 Java 更易于维护,因为该语言的默认设置有利于可维护性。就像我在维护方面更喜欢 F# 而不是 C#,因为它在该领域走得更远,默认情况下是不可变的。
      • @Jared,这是一个故意的错误类比,以回应您在隐藏成本和复杂性之间的错误关联。更简单的事情几乎总是有隐藏的成本。我认为应该很明显,我指的是它的使用简单性,而不是可维护性,特别是因为我指出 C# 更安全。作为记录,我也更喜欢 C#,但很难否认不必声明使用它比必须声明使用它更简单。
      【解决方案6】:

      Anders Hejlsberg:(C# 首席架构师)

      有几个原因。一个是 表现。我们可以观察到 人们用Java编写代码,他们忘记了 将他们的方法标记为最终方法。 因此,这些方法是虚拟的。 因为它们是虚拟的,所以它们不 表现也不错。有只是 与相关的性能开销 是一种虚拟方法。那是一个 问题。

      一个更重要的问题是版本控制。 有两种思想流派 虚方法。学术学校 思想说:“一切都应该是 虚拟的,因为我可能想要 总有一天会推翻它。”务实的 思想流派,来自 构建可以运行的真实应用程序 现实世界说,“我们必须 非常小心我们所做的 虚拟的。”

      当我们在 平台,我们做了很多 承诺它如何在 未来。对于非虚方法,我们 保证当你打电话给这个时 方法,x 和 y 会发生。什么时候我们 在 API 中发布虚拟方法,我们 不仅承诺当你打电话 这种方法,x 和 y 都会发生。我们 还承诺当你覆盖 此方法,我们将在此调用它 关于特定的顺序 这些其他的和国家将是 在这个和那个不变量中。

      每次你在 API 中说虚拟时, 您正在创建一个回调挂钩。作为 操作系统或 API 框架设计师, 你必须非常小心 那。您不希望用户覆盖 并钩在任意点 一个 API,因为你不一定 做出那些承诺。人们可能 不完全理解他们的承诺 当他们做某事时正在做 虚拟的。

      【讨论】:

      • 如果我在简历中看到一个同时使用 Java 和 .Net 的人,这是一个很好的面试问题 :) +1
      • 不幸的是,并不是每个设计 API 的人都会真正考虑什么应该是虚拟的,什么不应该是虚拟的。我来到这里(作为一个主要是 Java 程序员)想了解为什么我改变单个方法行为的唯一选择是下载和修改使用我想要覆盖其方法的类的整个软件堆栈的源代码。不过,这个问题的答案很好!
      猜你喜欢
      • 1970-01-01
      • 2019-09-24
      • 2022-09-25
      • 1970-01-01
      • 2011-09-30
      • 1970-01-01
      • 1970-01-01
      • 2014-11-01
      • 1970-01-01
      相关资源
      最近更新 更多