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

4
Arrays.asList(..)返回一个包装在数组周围的List。这个包装器有固定的大小,直接由数组支持,因此尝试修改列表的add()或其他函数的调用将引发UnsupportedOperationException异常。
开发人员经常对此感到惊讶,这可以从stackoverflow上的问题中看出。
然而,根据Liskov替换原则(LSP),List接口具有应该对所有List派生类工作得像预期一样的add()方法。
Arrays.asList()返回的类型是否违反了Liskov替换原则?

1
List.add 被记录为可选操作。 - Andy Turner
@AndyTurner 但是可选操作不是违反LSP原则吗? - Gonen I
这不是一个“论坛”,请不要称之为。 - Arnav Borborah
1
它们并不是真正的“可选操作”,因为它们可能不存在:它们始终存在,只是可能会抛出“UnsupportedOperationException”。这是记录下来的行为;只是许多人在接受“List”作为参数时没有考虑到这种情况。 - Andy Turner
3个回答

7
严格来说,它应该是违反了LSP原则,因为LSP没有可选接口成员的概念:方法要么是接口的一部分,要么不是接口的一部分。
然而,Java Class Library明确允许在指定某些接口方法为可选时违反LSP。 List<T>.add() 就是这样的一个方法。其他改变列表的方法(例如 addAllremove等)也被标记为可选。
基本上,Java库的设计者采取了一种捷径:他们没有为可变列表创建单独的接口(扩展只读列表),而是选择将操作 "标记为可选"。此外,他们并没有提供一种方法来测试列表实例是否为只读,因此您唯一的选择是捕获运行时异常,这是一个非常糟糕的想法。这意味着您必须跟踪列表的来源,并仅在您对列表来源有100%的确定性时执行可选操作。

这个解释也是我倾向的方向。 - Gonen I
2
@user889742 对的 - LSP 没有“可选操作”的概念,一切都是完全二进制的。Java 库设计者采取了一种捷径,没有为只读列表定义单独的接口,从而创建了这个混乱的可选操作。 - Sergey Kalinichenko
嗨@Sergey。我有一个抽象类,其中包含数据库修改和获取操作。但是有些子类不应该修改数据。我应该采取捷径还是分离操作? - Arash

5

我认为这并不违反LSP原则。

LSP原则指,所有实现一个给定接口的类的实例都可以互换使用。

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

抛出

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

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

如果不这样做,就不能正确使用API。


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

我只是认为遵循规定的行为不会违反LSP原则。


谢谢。但我担心,如果您可以用文档来捍卫LSP违规行为,那么您可以在Rectangle.setWidth // 注意,可能不适用于所有矩形的派生物上编写,并从中派生一个正方形,说这符合LSP。 - Gonen I
1
@user889742 是的,您可以这样做,而您类的用户需要处理它。理想情况下,您应该通过公认的文档约定来传达它,比如Javadoc中的@throws段落;但实际上,您在类的公共文档中编写的任何内容都是代码应该编写的行为契约。 - Andy Turner
@dasblinkenlight 我并不是在推荐捕获异常。我是说你需要意识到它可能会发生。 - Andy Turner
@dasblinkenlight 但我并不是说要捕获异常,我只是说“处理该情况”。如果我说“如果你不这样做,那你就忽略了该类的契约”,你会更开心吗? - Andy Turner

0

如果你从技术角度来看,那么当然是一种违规行为。LSP(里氏替换原则)指出,一个糟糕的设计就是继承类无法使用超类方法的设计。但是Java并不总是关心这些违规行为。正如你所建议的那样,这经常会导致开发人员的混淆。例如add()方法和remove()方法都适用于不可变列表,而且这是不可改变的。好的一面是至少会抛出异常。

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


谢谢。我理解了。 - Gonen I
如果是,请标记一个正确答案。 - Arnav Borborah

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接