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

Vit*_*liy 4 java extension-methods api-design java-8 default-method

所以我们有默认方法,也被称为防御方法和'虚拟扩展方法'.

虽然我很欣赏默认方法的巨大价值(在某些方面甚至比它们的C#对应物更强大)但我想知道什么是允许扩展现有接口而不访问其源代码的决定.

在他的一个答案中, Brian Goetz提到默认方法是为了方便和界面演变而设计的.因此,如果我们编写一个接口,我们可以在那里填充我们通常必须放在一个单独的类中的各种实用方法.那么为什么不加倍努力并允许它不受我们控制的接口?

Jea*_*nal 7

虽然Brian Goetz提到默认方法也会用于方便,但这并不意味着它是默认方法的第一个目标.

提醒一下:为了支持界面演化,存在默认方法,这是面对lambda引入时非常需要的东西(因为引入lambda使用方法通常意味着应该添加新方法).

您正在撰写您的帖子,就好像扩展方法没有任何缺点,因为它们存在于C#中,但是:

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

    • 它们没有出现在课程文档中;
    • 他们不是通过反思课程来暴露的;
    • 当您在代码中看到扩展方法的使用时,如果没有IDE,您无法知道扩展方法的来源;
  • 扩展方法本质上不允许某些标准的消歧机制:

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

    • 一旦扩展方法由库定义,该库的客户端就无法更改或调整该扩展方法的实现;
    • 如果客户端为他无法控制的类型定义了扩展方法,那么在库定义相同的扩展方法的那天,他的手臂上会出现兼容性问题.这种可能性取决于扩展方法的作用以及该库的演化策略.

在这三个缺点中,Java 8中存在的默认方法都没有.

您应该注意,在C#中包含扩展方法的重要原因是启用LINQ.由于Java为其流提供了另一种设计,我没有看到Java语言设计者将其添加为理所当然.

  • 为清楚起见,我将补充说,前两个缺点源于从外部注入而不是成为类定义的一部分; 第三个对应于C#的扩展方法是静态的而不是虚拟的. (4认同)
  • 我不会说默认方法的存在主要是为了使功能接口适用于更多现有接口.它们的存在主要是为了能够在不破坏与现有实现的向后兼容性的情况下向现有接口添加新方法. (2认同)

Bri*_*etz 7

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

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

如果您已经习惯了C#方法,那么很自然地认为这是"正确"或"正常"或"真实"的方式,但实际上,这只是众多可能方式中的一种.正如其他海报所指出的那样,与Java的默认方法相比,C#扩展方法有一些非常严重的缺点(例如,反馈可发现性差,通过文档可发现性差,不可覆盖,需要特殊的冲突管理规则).因此,相比之下,Java玻璃在这里已经超过了一半.