Arrays.asList违反Liskov替换原则吗?

时间:2016-09-03 22:32:28

标签: java arrays list liskov-substitution-principle

Arrays.asList(..)返回数组的List包装器。此包装器具有固定大小,并由数组直接支持,因此对add()或其他尝试修改列表的函数的调用将抛出UnsupportedOperationException。

开发人员经常对此感到惊讶,这从stackoverflow中的问题可以看出。

然而,根据Liskov替换原则(LSP),List接口有一个add()方法,对于List的所有派生应该不出所料。

Arrays.asList()返回的类型是违反Liskov替换原则的一个例子吗?

3 个答案:

答案 0 :(得分:4)

严格地说,因为LSP没有可选接口成员的概念:方法是接口的一部分,或者它不是接口的一部分。

但是,Java类库在将某些接口方法指定为可选时明确允许违反LSP。 List<T>.add()就是这样一种方法。其他变异方法(addAllremove等)也标记为可选。

基本上,Java库的设计者采用了一种捷径:不是为可变列表创建一个单独的接口(扩展只读列表),而是指定一个操作“可选”。此外,它们没有为您提供一种方法来测试列表实例是否为只读,因此您唯一的选择是捕获运行时异常,这是一个非常糟糕的主意。这相当于您必须跟踪列表的来源,并且只有在您100%确定列表来源时才执行可选操作。

答案 1 :(得分:3)

我认为这不违反LSP。

LSP表示实现给定接口的所有类实例都可以互换使用。

List.add(以及其他变异方法)的文档明确指出该方法的实现可能会抛出UnsupportedOperationException

  

抛出

     

UnsupportedOperationException - 如果此列表不支持添加操作

因此,如果您要在来自未知来源的List实例上调用此方法,则需要处理add抛出UnsupportedOperationException的情况

如果您没有,则表示您未正确使用API​​。

这并不是说我喜欢这个设计。我认为尝试调用该方法的唯一方法是检测实例上不支持任何给定方法是垃圾。我的意思是,哎呀,给我们一个isAddSupported()方法(或类似的)。

我只是没有看到记录在案的行为可能违反了LSP。

答案 2 :(得分:0)

如果您以非常技术的方式考虑它,那么当然这是违规行为。 LSP声明一个糟糕的设计是继承的类不能使用超类方法的设计。但是,Java并不总是关心违规行为。正如您所建议的那样,这通常是开发人员混淆的一种方式。 add()方法就是一个例子,remove()也是如此。这两个都可用于不可变列表,并且无法更改。好消息是至少会抛出异常。

进一步阅读:http://c2.com/cgi/wiki?UnmodifiableListIsStupidAndItBreaksLsp