Java 继承、组合和默认方法
人们通常承认,通过继承扩展接口的实现并不是最佳实践,而组合(例如从头开始重新实现接口)更易于维护 这是因为接口契约强制用户实现所有所需的功能。然而,在Java8中,默认方法提供了一些可以“手动”重写的默认行为。考虑下面的例子:我想设计一个用户数据库,它必须具有列表的功能。为了提高效率,我选择使用ArrayList来支持它Java 继承、组合和默认方法,java,inheritance,java-8,composition,default-method,Java,Inheritance,Java 8,Composition,Default Method,人们通常承认,通过继承扩展接口的实现并不是最佳实践,而组合(例如从头开始重新实现接口)更易于维护 这是因为接口契约强制用户实现所有所需的功能。然而,在Java8中,默认方法提供了一些可以“手动”重写的默认行为。考虑下面的例子:我想设计一个用户数据库,它必须具有列表的功能。为了提高效率,我选择使用ArrayList来支持它 public class UserDatabase extends ArrayList<User>{} 公共类UserDatabase扩展了ArrayList{}
public class UserDatabase extends ArrayList<User>{}
公共类UserDatabase扩展了ArrayList{}
这通常不会被认为是一个伟大的实践,如果你真的想要列表的全部功能,并且遵循通常的“组合优于继承”的格言,你会更愿意:
public类UserDatabase实现列表{
//在这里实现,使用ArrayList类型字段或装饰器模式等。
}
但是,如果不注意,一些方法,例如spliterator()将不需要重写,因为它们是列表接口的默认方法。需要注意的是,List的spliterator()方法的性能比ArrayList的spliterator()方法差得多,后者已针对ArrayList的特定结构进行了优化
这迫使开发人员
所以问题是:在这种情况下,一个人应该更喜欢组合而不是继承,这仍然是“正确的”吗 我不太明白这里的大问题。您仍然可以使用ArrayList支持
UserDatabase
,即使不扩展它,也可以通过委托获得性能。您不需要扩展它来获得性能
public class UserDatabase implements List<User>{
private ArrayList<User> list = new ArrayList<User>();
// implementation ...
// delegate
public Spliterator() spliterator() { return list.spliterator(); }
}
public类UserDatabase实现列表{
private ArrayList list=new ArrayList();
//实施。。。
//委派
public Spliterator()Spliterator(){return list.Spliterator();}
}
你的两点并没有改变这一点。如果您知道“ArrayList有它自己的、更高效的spliterator()实现”,那么您可以将其委托给您的支持实例,如果您不知道,那么默认方法会处理它
我仍然不确定实现列表接口是否真的有意义,除非您显式地创建了一个可重用的集合库。最好为这种一次性的API创建自己的API,这种API将来不会通过继承(或接口)链出现问题。我不能为每种情况提供建议,但对于这种特殊情况,我建议根本不要实现
列表。UserDatabase.set(int,User)
的目的是什么?是否确实要用全新的用户替换备份数据库中的第i个条目?添加(int,User)
怎么样?在我看来,您应该将其实现为只读列表(在每个修改请求上抛出UnsupportedOperationException
),或者只支持一些修改方法(例如add(User)
受支持,但add(int,User)
不受支持)。但后一种情况会让用户感到困惑。最好提供您自己的修改API,它更适合您的任务。对于读取请求,最好返回一个用户流:
我建议创建一个返回流的方法
:
public class UserDatabase {
List<User> list = new ArrayList<>();
public Stream<User> users() {
return list.stream();
}
}
公共类用户数据库{
列表=新的ArrayList();
公共流用户(){
返回list.stream();
}
}
注意,在这种情况下,您可以完全自由地在将来更改实现。例如,用<代码> > TreSeT或
default
方法的专门化在继承情况下完全中断的可能性和default
方法在委托情况下不能以最大性能工作的可能性之间进行权衡。我认为,答案应该是显而易见的
由于您的问题是关于新的默认方法是否会改变这种情况,因此应该强调的是,与以前不存在的方法相比,您谈论的是性能下降。让我们继续看排序
示例。如果您使用委派,并且不重写default
排序方法,则default
方法的性能可能低于优化的ArrayList.sort
方法,但在Java 8之前,后者不存在,并且未针对ArrayList
进行优化的算法是标准行为
因此,在Java8下,您并没有失去委托的性能,只要不重写default
方法,您就不会获得更多。我认为,由于其他改进,性能仍将优于Java7(没有default
方法)
Stream
API不易比较,因为在java8之前不存在该API。但是,很明显,类似的操作,例如,如果您手动实现缩减,除了通过委托列表的迭代器
之外别无选择,必须防止public class UserDatabase {
List<User> list = new ArrayList<>();
public Stream<User> users() {
return list.stream();
}
}