“API 设计就像性:犯一个错误并在你的余生中支持它” (推特上的乔什·布洛赫)
Java 库中有很多设计错误。Stack extends Vector
(讨论),我们不能在不造成损坏的情况下解决这个问题。我们可以尝试弃用Integer.getInteger
(讨论),但它可能会永远存在。
尽管如此,可以在不造成损坏的情况下进行某些类型的改造。
Effective Java 第 2 版,第 18 项:优先使用接口而不是抽象类:现有类可以很容易地改造以实现新接口”。
示例:String implements CharSequence
、Vector implements List
等。
Effective Java 第 2 版,第 42 条:明智地使用可变参数:您可以改进现有的方法,该方法将数组作为其最终参数以采用可变参数,而不会对现有客户端产生影响。
一个(不)著名的例子是Arrays.asList
,它引起了混乱(讨论),但没有破坏。
这个问题是关于一种不同类型的改造:
你能在void
不破坏现有代码的情况下改造一种方法来返回一些东西吗?
我最初的预感是肯定的,因为:
- 返回类型不影响编译时选择的方法
- 请参阅:JLS 15.12.2.11 - 未考虑返回类型
- 因此,更改返回类型不会更改编译器选择的方法
- 改装从
void
退货是合法的(但不是相反!)
- 即使您使用反射,
Class.getMethod
也无法区分返回类型
但是,我想听听其他在 Java/API 设计方面更有经验的人进行更彻底的分析。
附录:动机
正如标题中所建议的,一个动机是促进流畅的界面风格编程。
考虑这个简单的代码片段,它打印一个打乱的名字列表:
List<String> names = Arrays.asList("Eenie", "Meenie", "Miny", "Moe");
Collections.shuffle(names);
System.out.println(names);
// prints e.g. [Miny, Moe, Meenie, Eenie]
已经Collections.shuffle(List)
声明返回输入列表,我们可以这样写:
System.out.println(
Collections.shuffle(Arrays.asList("Eenie", "Meenie", "Miny", "Moe"))
);
如果它们要返回输入列表而不是,等,那么使用其他方法Collections
会更愉快。事实上,拥有和返回尤其不幸,因为它剥夺了我们编写表达代码的能力,void
例如像这样:reverse(List)
sort(List)
Collections.sort
Arrays.sort
void
// DOES NOT COMPILE!!!
// unless Arrays.sort is retrofitted to return the input array
static boolean isAnagram(String s1, String s2) {
return Arrays.equals(
Arrays.sort(s1.toCharArray()),
Arrays.sort(s2.toCharArray())
);
}
当然,这种void
阻碍流畅性的返回类型不仅限于这些实用方法。这些java.util.BitSet
方法也可以编写为返回this
(alaStringBuffer
和StringBuilder
) 以促进流畅性。
// we can write this:
StringBuilder sb = new StringBuilder();
sb.append("this");
sb.append("that");
sb.insert(4, " & ");
System.out.println(sb); // this & that
// but we also have the option to write this:
System.out.println(
new StringBuilder()
.append("this")
.append("that")
.insert(4, " & ")
); // this & that
// we can write this:
BitSet bs1 = new BitSet();
bs1.set(1);
bs1.set(3);
BitSet bs2 = new BitSet();
bs2.flip(5, 8);
bs1.or(bs2);
System.out.println(bs1); // {1, 3, 5, 6, 7}
// but we can't write like this!
// System.out.println(
// new BitSet().set(1).set(3).or(
// new BitSet().flip(5, 8)
// )
// );
不幸的是,与StringBuilder
/不同StringBuffer
,所有的BitSet
mutators 都返回void
。