Arrays.asList(..)
返回一个包装在数组周围的List。这个包装器有固定的大小,直接由数组支持,因此尝试修改列表的add()或其他函数的调用将引发UnsupportedOperationException异常。开发人员经常对此感到惊讶,这可以从stackoverflow上的问题中看出。
然而,根据Liskov替换原则(LSP),List接口具有应该对所有List派生类工作得像预期一样的add()方法。
Arrays.asList()返回的类型是否违反了Liskov替换原则?
Arrays.asList(..)
返回一个包装在数组周围的List。这个包装器有固定的大小,直接由数组支持,因此尝试修改列表的add()或其他函数的调用将引发UnsupportedOperationException异常。List<T>.add()
就是这样的一个方法。其他改变列表的方法(例如 addAll
,remove
等)也被标记为可选。我认为这并不违反LSP原则。
LSP原则指,所有实现一个给定接口的类的实例都可以互换使用。
List.add
(以及其他变异方法)的文档明确说明,该方法的实现可能会抛出UnsupportedOperationException
异常。
抛出
UnsupportedOperationException
- 如果此列表不支持添加操作
因此,如果您要在未知来源的List
实例上调用此方法,则需要处理add
抛出UnsupportedOperationException
的情况。
如果不这样做,就不能正确使用API。
这并不意味着我喜欢这种设计。我认为,唯一检测到任何给定方法不受实例支持的方法是尝试调用该方法,这是垃圾的。我的意思是,给我们一个isAddSupported()
方法(或类似的方法)。
我只是认为遵循规定的行为不会违反LSP原则。
@throws
段落;但实际上,您在类的公共文档中编写的任何内容都是代码应该编写的行为契约。 - Andy Turner如果你从技术角度来看,那么当然是一种违规行为。LSP(里氏替换原则)指出,一个糟糕的设计就是继承类无法使用超类方法的设计。但是Java并不总是关心这些违规行为。正如你所建议的那样,这经常会导致开发人员的混淆。例如add()
方法和remove()
方法都适用于不可变列表,而且这是不可改变的。好的一面是至少会抛出异常。
进一步阅读:http://c2.com/cgi/wiki?UnmodifiableListIsStupidAndItBreaksLsp
List.add
被记录为可选操作。 - Andy Turner