Java 8向类添加扩展/默认方法

Che*_*tah 36 java default-method

我正在寻找一个等同于C#扩展方法功能的java.现在我一直在阅读Java 8的默认方法,但据我所知,我只能将它们添加到接口......

...是否有任何语言功能允许我为没有实现接口的最终类​​编写扩展方法?(我宁愿不用包装......)

Rhy*_*ous 26

Java没有扩展方法.默认方法不是扩展方法.我们来看看每个功能.

Java的默认方法

问题解决了:

  1. 许多对象可以实现相同的接口,并且它们都可以对方法使用相同的实现.基类可以解决此问题,但前提是接口实现者还没有基类,因为java不支持多重继承.
  2. API希望在不破坏API使用者的情况下向接口添加方法.添加具有默认实现的方法可解决此问题.

Java的默认方法是向接口添加默认实现的功能.因此,扩展接口的对象不必实现该方法,它们只能使用默认方法.

interface IA { default public int AddOne(int i) { return i + 1; } }
Run Code Online (Sandbox Code Playgroud)

任何实现IA的对象都不必实现AddOne,因为有一个默认方法可以使用.

public class MyClass implements IA { /* No AddOne implementation needed */ } 
Run Code Online (Sandbox Code Playgroud)

C#缺少此功能,通过添加此功能可以大大改善.(更新:C#8中的功能)

C#的扩展方法

问题解决了:

  1. 能够向密封类添加方法.
  2. 能够在不强制继承的情况下从第三方库向类添加方法.
  3. 能够在由于约定原因而不允许模型类中的方法的环境中向模型类添加方法.
  4. 智能感知能够向您呈现这些方法.

示例:类型字符串是C#中的密封类.密封时不能从字符串继承.但是您可以添加可以从字符串调用的方法.

var a = "mystring";
a.MyExtensionMethed()
Run Code Online (Sandbox Code Playgroud)

Java缺少此功能,并且通过添加此功能可以大大改进.

结论

Java的默认方法和C#的扩展方法功能甚至没有相似之处.它们完全不同,解决了完全不同的问题.


gex*_*ide 23

C#扩展方法只是静态方法的语法糖,它将扩展类型作为第一个参数.Java默认方法是完全不同的.要模仿C#扩展方法,只需编写常用的静态方法.但是,你不会有合成糖; Java没有此功能.

Java默认方法是真正的虚方法.例如,它们可以被覆盖.考虑XI声明默认foo()方法的接口继承的类.如果X或其任何超类声明没有自己的foo()方法,那么X将获得foo()执行I.现在,子类YX可以覆盖X.foo()像通常的方法.因此,默认方法不仅仅是语法糖.它们是方法覆盖和继承机制的真正扩展,无法通过其他语言功能进行模仿.

默认方法甚至需要特殊的VM支持,因此它们甚至不是仅编译器的特性:在类加载期间,必须检查类的层次结构以确定它将继承哪些默认方法.因此,此决定是在运行时进行的,而不是在编译时进行的.关于它的一个很酷的事情是,当它继承的接口获得一个新的默认方法时,你不必重新编译一个类:VM将在类加载时将新方法分配给它.

  • @Cheetah:不,他们远不是语法糖.它们是真正的扩展,甚至需要VM支持.看我的编辑. (6认同)

Bri*_*etz 21

C#扩展方法是静态使用站点,而Java的默认方法是虚拟声明站点.

我相信你所希望的是能够将一个方法"猴子修补"到你无法控制的类中,但是Java并没有给你那个(通过设计;它被考虑并被拒绝了.)

默认方法相对于C#方法的另一个好处是它们是可反射发现的,实际上从外部看,与"常规"接口方法没有任何不同.

与Java的默认方法相比,C#的扩展方法的一个优点是,使用C#的具体泛型,扩展方法被注入到类型中,而不是类中,因此您可以将sum()方法注入List<int>.

最重要的是,Java的默认方法和C#的扩展方法之间的主要哲学区别在于C#允许您将方法注入到您无法控制的类型中(这对于开发人员来说肯定是方便的),而Java的扩展方法是API的一流部分.它们出现在它们中(它们在界面中声明,它们是可反射发现的等等).这反映了几个设计原则; 库开发人员应该能够保持对API的控制,并且库的使用应该是透明的 - 调用x()类型的方法Y应该在任何地方都是一样的.

  • @TWiStErRob很多原因,一个很重要的原因是:API设计者应该控制他们的API.另一个是透明度; 当有人阅读代码看到xm()时,他们应该能够在X的文档中找到m(),而不是在十几个范围之一中的一些随机monkeypatch(这是一个距离动作).相关地,有上下文自由; Xm()在任何共享相同X定义的作用域中应该表示相同的东西,但是使用使用站点扩展方法时,它不会."这将是方便的"不会远远接近克服这些重大障碍. (8认同)
  • 魔术8球说:"不要指望它." (7认同)
  • >>"而Java遵循库开发人员应该能够保持对其API的控制的原则"这种推理方式无效.与静态实用程序方法相比,扩展方法不再更改API.扩展方法只是使实用程序方法更容易访问,从扩展用户的角度来看,为了方便*,本质上给出了扩展API的*幻觉*. (7认同)
  • 我个人会用它来扩展使用常用的简单方法的内置函数和库.1. API追赶时的Polyfill:`String.isEmpty()`(迟到10年),`Comparator.reversed()`(迟到16年).2.添加设计者不会添加的方法,因为它会"污染"API:`Collection.groupBy(v-> k)`,`InputStream.copyTo(OutputStream)`.不要误会我的意思,我喜欢Java就是KISS,这真是太神奇了[它实际建立的类很少](https://www.parleys.com/tutorial/java-the-good-bad-ugly-parts ),但有时我需要更多一点. (5认同)
  • 这是一个很好的,但我觉得这限制了图书馆用户的自由.我同意"API设计者应该控制他们的API",但对我来说,并不等于这些API的用户无法按照他们想要的方式扩展它们.我同意C#式扩展可以被滥用(例如`((X)null).m()`可能不会抛出NPE :),就像`static import`s可以被过度使用一样.我认为如果以合理的方式使用这些功能有助于开发.IDE支持可以隐藏"它来自哪里?" 复杂. (3认同)
  • 你能链接到关于"考虑和拒绝"的讨论,我真的想明白为什么?每个库和应用程序都有自己的"CollectionUtils","IOTools","IteratorExtensions"等等......如果他们的方法可以通过实例调用,那就太好了. (2认同)
  • 你能链接到关于"考虑和拒绝"的讨论吗? (2认同)

Gee*_*Bee 7

可以使用一些技巧来扩展方法.

您可以试试Lombok或XTend.虽然扩展方法没有开箱即用的Java实现,但Lombok和XTend都提供了完全可行的解决方案.

Lombok是一个简单的独立代码处理框架,它使大多数批评的Java特定麻烦不那么痛苦,包括扩展方法:https: //projectlombok.org/features/experimental/ExtensionMethod.html

Xtend http://www.eclipse.org/xtend/向前推进了几年,并实现了一种语言,它结合了现代语言的最佳部分,例如Java和Java类型系统上的Scala.这允许在同一个项目中在Xtend中实现某些类,在Java中实现其他类.Xtend代码符合有效的Java代码,因此没有JVM魔法发生在幕后.另一方面,如果只缺少扩展方法,那就太多了.

JPropel https://github.com/nicholas22/jpropel-light使用Lombok在Java中实现LINQ样式扩展方法.这可能值得一看:)