不允许在Java8中使用站点注入扩展方法的设计考虑是什么?

不允许在Java8中使用站点注入扩展方法的设计考虑是什么?,java,java-8,extension-methods,api-design,default-method,Java,Java 8,Extension Methods,Api Design,Default Method,我们有默认方法,也称为defender方法和“虚拟扩展方法” 虽然我欣赏默认方法的巨大价值(在某些方面甚至比它们的C#同行更强大),但我想知道,为什么不允许在不访问源代码的情况下扩展现有接口 Brian Goetz提到,默认方法的设计非常方便,同时也考虑了接口的演变。因此,如果我们编写一个接口,我们可以在那里填充各种实用程序方法,这些方法通常必须放在一个单独的类中。那么,为什么不多做一点,允许它用于我们无法控制的接口呢?虽然Brian Goetz提到默认方法也将用于方便,但这并不意味着它是默认方

我们有默认方法,也称为defender方法和“虚拟扩展方法”

虽然我欣赏默认方法的巨大价值(在某些方面甚至比它们的C#同行更强大),但我想知道,为什么不允许在不访问源代码的情况下扩展现有接口


Brian Goetz提到,默认方法的设计非常方便,同时也考虑了接口的演变。因此,如果我们编写一个接口,我们可以在那里填充各种实用程序方法,这些方法通常必须放在一个单独的类中。那么,为什么不多做一点,允许它用于我们无法控制的接口呢?

虽然Brian Goetz提到默认方法也将用于方便,但这并不意味着它是默认方法的首要目标

提醒一下:默认方法的存在是为了支持接口演化,这在lambda的引入中是非常迫切需要的(因为lambda使用方法的引入通常意味着应该添加新方法)

您在撰写文章时,似乎C#中存在的扩展方法没有缺点,但是:

  • 扩展方法有一个巨大的可发现性问题:

    • 它们不会出现在类文档中
    • 他们不会在课堂上反思而暴露出来
    • 当您在代码中看到扩展方法的用法时,如果没有IDE,您将无法知道扩展方法来自何处
  • 扩展方法本质上不允许某些标准的消歧机制:

    • 当您使用扩展方法为类方法定义重载,而该重载或类似的扩展方法是由类的作者定义的时,会发生什么情况?案例1:您的代码通过简单的重新编译切换到使用新方法;案例2:您的代码不再编译
    • 因此,我不建议定义将扩展方法放在您无法控制的命名空间中,因为这样会使此类问题变得棘手。然而,这样做意味着不受控制的类型的扩展方法变得更加难以发现
  • 扩展方法不灵活:

    • 一旦库定义了扩展方法,该库的客户端就无法更改或调整该扩展方法的实现
    • 如果客户机为他不控制的类型定义了扩展方法,那么在库定义同一扩展方法的那一天,他将遇到兼容性问题。这种可能性取决于扩展方法所做的事情,以及该库的演化策略
在这三个缺点中,Java8中存在的默认方法没有任何缺点


您应该注意到,在C#中包含扩展方法的主要原因是为了启用LINQ。由于Java对其流进行了另一种设计,我认为Java语言设计师不会理所当然地添加它们。

这是由一种哲学信念驱动的:API设计师应该控制他们的API。虽然从外部向API中注入方法肯定很方便,但它破坏了API设计者对其API的控制。(这有时被称为“猴子修补”。)

在术语上:C#所谓的“可拓方法”只是可拓方法的一种形式,而不是可拓方法的定义;Java的默认方法也是扩展方法。主要区别在于:C#扩展方法是静态的,在使用现场注入;Java是虚拟和声明站点。其次,C#扩展方法被注入到类型中,而Java的默认方法是类的成员。(这允许您将
sum()
方法插入C#中的
List
,而不影响其他
List
实例化。)


如果你已经习惯了C#方法,很自然地会认为这是一种“正确”或“正常”或“真实”的方法,但实际上,这只是许多可能的方法之一。正如其他海报所指出的,与Java的默认方法相比,C#扩展方法有一些非常严重的缺点(例如,反射性可发现性差,通过文档可发现性差,不可重写,需要特别的冲突管理规则)。因此,相比之下,Java玻璃杯已经装满了一半以上

我不知道,但我知道是什么让我对这样一个特性感到愤怒:看到一些代码中使用的方法Foo.bar(),浏览Foo的javadoc,没有看到任何关于bar()方法的提及。我不认为它是基于观点的。我在问他们的意见是什么——有区别。这些注意事项可能出现在某些邮件列表中(尽管我没有找到任何邮件列表),也可能出现在Java架构师的头脑中,这些架构师监视此类问题。以“”为例,Stuart Marks回答了我的问题。值得一提的是,我害怕允许人们向他们无法控制的接口添加任意方法;库混乱是一个需要不断努力来避免的严重问题,而设计最好的库往往故意在其API中最小化。这是由一个哲学信念驱动的:API设计者应该控制他们的API。对现有API的修补破坏了API设计者对其API的控制。(另外:使用“扩展方法”来表示“C#调用的扩展方法”有些模糊;默认方法也是扩展方法。C#选择“静态”和“使用站点”作为其扩展方法;我们选择“虚拟”和“声明站点”。(另请注意:虚拟在这里显然更强大,因此对于喜欢C#口译的人来说,杯子是半满半空的。)@BrianGoetz,谢谢。我希望你能