Arrays.asList 是否违反里氏替换原则?

Is Arrays.asList a violation of Liskov Substitution Principle?

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

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

但是 List 接口有一个 add() 方法,根据 Liskov 替换原则 (LSP)

Arrays.asList()返回的类型是否是违反里氏替换原则的例子?

严格来说,是的,因为 LSP 没有可选接口成员的概念:方法要么是接口的一部分,要么不是接口的一部分。

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

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

如果你用非常专业的术语来考虑,那当然是违规的。 LSP 声明糟糕的设计是继承的 class 不能使用 superclass 方法。 Java 但是,并不总是关心违规行为。正如您所建议的,这通常是开发人员之间混淆的一种方式。 add() 方法就是一个例子,remove() 也是一个例子。这两个都可用于 immutable 列表,并且无法更改。好在至少抛出了一个异常。

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

我觉得不违反LSP

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

List.add(和其他变异方法)的文档清楚地指出 UnsupportedOperationException 可能会被该方法的实现抛出。

Throws

UnsupportedOperationException - if the add operation is not supported by this list

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

如果不这样做,则说明您没有正确使用 API。


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

我只是不认为以下记录在案的行为会违反 LSP。