你还在使用Guava的Lists.newArrayList()吗
Guava
说起Guava,做Java开发的应该没人不知道吧,毕竟“google出品,必属精品”。虽然应该没有Spring那样让Javaer无法避开,但是其中很多工具类的封装还是让人欲罢不能。
而我们今天要说的就是Guava中对集合类型构造方法的封装,拿Lists.newArrayList()举例。
源码
事情的起因其实是某次Code Review中同事说到的一句话,他问“你们为什么不直接用new ArrayList(),却喜欢用Lists.newArrayList()?”。多么朴实无华的问题,我当然要趁此机会推销一波,“使用Lists.newArrayList()多有逼格啊”,“内部其实就是new了个List,性能没区别的”。然后他慢悠悠地说了后半句:“源码注释写着不建议使用”。
说实话,源码我肯定是看过的,毕竟很简单,只要你点进去过,你就敢说自己看过源码。这就是我之前视角中的源码,除了方法调用栈深了一层,跟new ArrayList()有半毛钱区别吗?
public static <E> ArrayList<E> newArrayList() {
return new ArrayList<>();
}
但其实完整地带注释的方法源码长这样,版本是28.1-jre。
/**
* Creates a <i>mutable</i>, empty {@code ArrayList} instance (for Java 6 and earlier).
*
* <p><b>Note:</b> if mutability is not required, use {@link ImmutableList#of()} instead.
*
* <p><b>Note for Java 7 and later:</b> this method is now unnecessary and should be treated as
* deprecated. Instead, use the {@code ArrayList} {@linkplain ArrayList#ArrayList() constructor}
* directly, taking advantage of the new <a href="http://goo.gl/iz2Wi">"diamond" syntax</a>.
*/
@GwtCompatible(serializable = true)
public static <E> ArrayList<E> newArrayList() {
return new ArrayList<>();
}
重点是注释的后半部分,对于Java7以及之后的版本,本方法不必要且应被弃用,建议直接使用ArrayList的构造方法,可以利用新的“菱形语法”的优势。
一开始看到菱形语法这个新名词我是懵逼的,还好注释里很贴心地贴了个链接,其实就是泛型中的<>,这玩意儿就叫菱形语法。那为啥两个看上去没啥区别的方法又和这个扯上关系了呢?
泛型实例创建时的类型推断
泛型是JDK1.5推出的一个语法糖,但其实我本人觉得还是挺有用的,可以在编译期帮助我们提前发现代码的问题。平时我们泛型见得最多的地方应该就是集合了,比如List<String>就代表这个列表只能存放String类型的变量,如果放错了,我相信你的ide就会友好地提醒你,除非你用记事本编程。
在idea中新建一个ArrayList试试,为了直观地显示idea的提示,我这边使用截图。
- 第一行不加菱形有一个警告,提示你应该使用泛型
- 第二行,perfect
- 第三行,提示String多余
我这边使用的是JDK8,按照Oracle的官方说明,在JDK7其实就有这种现象了:
In Java SE 7, you can substitute the parameterized type of the constructor with an empty set of type parameters (<>):
Map<String, List<String>> myMap = new HashMap<>();
还有一句比较关键的话
Note that to take advantage of automatic type inference during generic class instantiation, you must specify the diamond.
这就是为啥第一行会报警告的原因,因为不加这个菱形就不做类型推断。
为啥不推荐用
看到这里是不是还是一脸懵逼,不知道Guava为啥不推荐使用,因为Lists.newArrayList()的内部就是new ArrayList<>(),带菱形的,完全符合JDK7及以上版本的要求。所以我只好把Oracle的解释全文看了一遍,并且找到了关键点:
List<String> list = new ArrayList<>();
list.add("A");
// The following statement should fail since addAll expects
// Collection<? extends String>
list.addAll(new ArrayList<>());
In comparison, the following example compiles:
// The following statements compile:
List<? extends String> list2 = new ArrayList<>();
list.addAll(list2);
是的,如果不明确在构造方法指明泛型类型,也就是单纯的一个菱形,那类型推断功能是受限的。拿上面的举例,new ArrayList<>()并不能根据上上下推断出想要构造的集合是一个List<? extends String>。所以按照官方的意思,在这种情况下用Lists.newArrayList()就搞不定了。
上面的例子是Oracle的官方说法,那到底是不是呢?idea中试一把。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("A");
// The following statement should fail since addAll expects
// Collection<? extends String>
list.addAll(new ArrayList<>());
}
跑起来完全没问题。。。当然,将allAll方法中的new ArrayList<>()替换为Lists.newArrayList()也是没问题的。
写在最后
从我个人观点来看,我觉得这应该不是idea做的优化,这么看来,官方的说法还是有待考证。
就算官方的说法是对的,那么也只是限制了某些编译期就会报错的场景,似乎问题不大,所以到底用不用Guava的集合创建方法,还是看大家的个人喜好吧。