继承,组合和默认方法 [英] Inheritance, composition and default methods

查看:126
本文介绍了继承,组合和默认方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通常承认,通过继承扩展接口的实现不是最佳实践,并且该组合(例如,从头开始再次实现接口)更加可维护。

It is usually admitted that extending implementations of an interface through inheritance is not best practice, and that composition (eg. implementing the interface again from scratch) is more maintenable.

这是有效的,因为接口契约会强制用户实现所有所需的功能。但是在java 8中,默认方法提供了一些可以手动覆盖的默认行为。请考虑以下示例:我想设计一个用户数据库,该数据库必须具有List的功能。出于效率目的,我选择通过ArrayList来支持它。

This works because the interface contract forces the user to implement all the desired functionality. However in java 8, default methods provide some default behavior which can be "manually" overriden. Consider the following example : I want to design a user database, which must have the functionalities of a List. I choose, for efficiency purposes, to back it by an ArrayList.

public class UserDatabase extends ArrayList<User>{}

这通常不被认为是一种很好的做法,如果实际上需要List的全部功能并遵循通常的遗传构成的座右铭,那就更愿意了:

This would not usually be considered great practice, and one would prefer, if actually desiring the full capabilities of a List and following the usual "composition over inheritance" motto :

public class UserDatabase implements List<User>{
  //implementation here, using an ArrayList type field, or decorator pattern, etc.
}

但是,如果不注意,一些方法,如spliterator()不需要重写,因为它们是List接口的默认方法。问题是,List的spliterator()方法执行得比ArrayList的spliterator()方法差得多,后者已针对ArrayList的特定结构进行了优化。

However, if not paying attention, some methods, such as spliterator() will not be required to be overridden, as they are default methods of the List interface. The catch is, that the spliterator() method of List performs far worse than the spliterator() method of ArrayList, which has been optimised for the particular structure of an ArrayList.

这迫使开发人员


  1. 请注意ArrayList有自己的,更有效地实现spliterator(),并手动覆盖他自己的List实现的spliterator()方法或

  2. 使用默认方法失去了大量的性能。

所以问题是:在这种情况下,人们应该更喜欢构成而非继承吗?

So the question is : is it still "as true" that one should prefer composition over inheritance in such situations ?

推荐答案

在开始考虑性能之前,我们总是应该考虑正确性,即在你的问题中我们应该考虑使用继承而不是代表团暗示。 此EclipseLink / JPA问题已经说明了这一点。由于继承,如果尚未填充延迟填充列表,则排序(同样适用于流操作)不起作用。

Before start thinking about performance, we always should think about correctness, i.e. in your question we should consider what using inheritance instead of delegation implies. This is already illustrated by this EclipseLink/ JPA issue. Due to the inheritance, sorting (same applies to stream operation) don’t work if the lazily populated list hasn’t populated yet.

所以我们必须在两者之间进行权衡专有化(覆盖新的默认方法)在继承情况下完全破坏的可能性以及默认的可能性方法不适用于委托案例中的最大性能。我认为答案应该是显而易见的。

So we have to trade off between the possibility that the specializations, overriding the new default methods, break completely in the inheritance case and the possibility that the default methods don’t work with the maximum performance in the delegation case. I think, the answer should be obvious.

因为你的问题是关于新的默认方法改变的情况,应该强调的是,与以前甚至不存在的东西相比,你所说的是性能下降。让我们留在 sort 示例。如果您使用委托并且不覆盖默认排序方法,则默认方法的性能可能低于优化 ArrayList.sort 方法,但在Java 8之前,后者不存在,并且未针对 ArrayList 进行优化的算法是标准行为。

Since your question is about whether the new default methods change the situation, it should be emphasized that you are talking about a performance degradation compared to something which did not even exist before. Let’s stay at the sort example. If you use delegation and don’t override the default sorting method, the default method might have lesser performance than the optimized ArrayList.sort method, but before Java 8 the latter did not exist and an algorithm not optimized for ArrayList was the standard behavior.

因此,在Java 8下,您不是失去性能,您只需当你没有覆盖默认方法时,没有获得更多。我认为,由于其他改进,性能仍然优于Java 7(没有默认方法)。

So you are not loosing performance with the delegation under Java 8, you are simply not gaining more, when you don’t override the default method. Due to other improvements, I suppose, that the performance will still be better than under Java 7 (without default methods).

Stream API不容易比较,因为在Java 8之前不存在API。但是,很明显类似的操作,例如如果你手动实施减少,除了通过你的代表团名单的 Iterator 之外别无选择,必须防范 remove()尝试,因此包装 ArrayList Iterator ,或使用大小() get(int)委托给支持 List 。因此,没有任何情况下,前$ 默认方法API可以表现出比Java 8的默认方法更好的性能API,因为过去没有 ArrayList - 特定优化。

The Stream API is not easily comparable as the API didn’t exist before Java 8. However, it’s clear that similar operations, e.g. if you implement a reduction by hand, had no other choice than going through the Iterator of your delegation list which had to be guarded against remove() attempts, hence wrap the ArrayList Iterator, or to use size() and get(int) which delegate to the backing List. So there is no scenario where a pre- default method API could exhibit better performance than the default methods of the Java 8 API, as there was no ArrayList-specific optimization in the past anyway.

那就是说,你的API设计可以通过以不同方式使用组合进行改进:不允许 UserDatabase 实施列表<用户> 根本。只需通过访问器方法提供列表。然后,其他代码不会尝试流式传输 UserDatabase 实例,而是覆盖访问器方法返回的列表。返回的列表可能是只读包装器,它提供了JRE本身提供的最佳性能,并在可行的情况下小心覆盖默认方法。

That said, your API design could be improved by using composition in a different way: by not letting UserDatabase implement List<User> at all. Just offer the List via an accessor method. Then, other code won’t try to stream over the UserDatabase instance but over the list returned by the accessor method. The returned list may be a read only wrapper which provides optimal performance as it is provided by the JRE itself and takes care to override the default methods where feasible.

这篇关于继承,组合和默认方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆