我们有默认方法,也称为防御方法和“虚拟扩展方法”。
虽然我很欣赏默认方法的巨大价值(在某些方面甚至比它们的 C# 对应物更强大),但我想知道在不访问其源代码的情况下允许扩展现有接口的决定是什么。
在 SO Brian Goetz 的一个答案中,他提到默认方法的设计非常方便以及接口演变。因此,如果我们编写一个接口,我们可以在其中填充通常必须放在单独的类中的各种实用方法。那么为什么不加倍努力并允许它用于不受我们控制的接口呢?
我们有默认方法,也称为防御方法和“虚拟扩展方法”。
虽然我很欣赏默认方法的巨大价值(在某些方面甚至比它们的 C# 对应物更强大),但我想知道在不访问其源代码的情况下允许扩展现有接口的决定是什么。
在 SO Brian Goetz 的一个答案中,他提到默认方法的设计非常方便以及接口演变。因此,如果我们编写一个接口,我们可以在其中填充通常必须放在单独的类中的各种实用方法。那么为什么不加倍努力并允许它用于不受我们控制的接口呢?
这是由一种哲学信念驱动的:API 设计者应该控制他们的 API。虽然从外部将方法注入 API 肯定很方便,但它破坏了 API 设计者对其 API 的控制。(这有时被称为“猴子补丁”。)
关于术语:C#所谓的“扩展方法”只是扩展方法的一种形式,并不是扩展方法的定义;Java 的默认方法也是扩展方法。主要区别在于: C# 扩展方法是静态的,并且是在使用站点注入的;Java 是虚拟的和声明站点。其次,C# 扩展方法被注入到类型中,而 Java 的默认方法是类的成员。(这允许您在 C#中注入sum()
方法而不影响其他实例化。) List<int>
List
如果您已经习惯了 C# 方法,那么很自然地会认为这是“正确”、“正常”或“真实”的方法,但实际上,这只是众多可能方法中的一种。正如其他海报所指出的那样,与 Java 的默认方法相比,C# 扩展方法有一些非常严重的缺点(例如,反射发现能力差、通过文档发现能力差、不可覆盖、需要临时冲突管理规则)。因此,相比之下,Java 玻璃杯在这里远远超过半满。
虽然 Brian Goetz 提到默认方法也将用于方便,但这并不意味着它是默认方法的第一个目标。
提醒一下:默认方法的存在是为了支持接口演化,这是在引入 lambda 时非常需要的东西(因为引入 lambda 使用方法通常意味着应该添加新方法)。
您正在写您的帖子,就好像它们存在于 C# 中的扩展方法没有缺点一样,但是:
扩展方法有一个巨大的可发现性问题:
扩展方法,就其本质而言,不允许一些标准的消歧机制:
扩展方法不灵活:
在这三个缺点中,Java 8 中存在的默认方法都没有。
您应该注意到,在 C# 中包含扩展方法的主要原因是启用 LINQ。由于 Java 对其流进行了另一种设计,我认为 Java 语言设计者不会理所当然地添加它们。