I was just looking at Guava's ImmutableList
and I noticed that the of()
method was overloaded 12 times.
It looks to me that all they needed was:
sta开发者_如何学运维tic <E> ImmutableList<E> of();
static <E> ImmutableList<E> of(E element); // not even necessary
static <E> ImmutableList<E> of(E... elements);
What's the reason for having so many similar variations?
Varargs and generics do not play nicely together. Varargs methods can cause a warning with generic arguments, and the overloads prevent that warning except in the rare case that you want to add more than 11 items to the immutable list using of()
.
The comments in the source say:
These go up to eleven. After that, you just get the varargs form, and whatever warnings might come along with it. :(
Note that Java 7's @SafeVarargs annotation was added specifically to eliminate the need for this sort of thing. A single of(E...)
method annotated with @SafeVarargs
could be used and would not give warnings with generic arguments.
There's also a performance reason. Every invocation of a varargs method causes an array allocation and initialization. If you have somehow determined that e.g. 95% of the calls are with 3 or less arguments and only 5% with 4 or more, then overloading like this
public static <E> ImmutableList<E> of();
public static <E> ImmutableList<E> of( E e );
public static <E> ImmutableList<E> of( E e1, E e2 );
public static <E> ImmutableList<E> of( E e1, E e2, E e3 );
public static <E> ImmutableList<E> of( E e1, E e2, E e3, E... es );
leads to a nice performance boost in 95% of the cases. Differently put, the average case performance goes up.
In addition to the other great answers here, there's a subtle runtime performance advantage (in addition to avoiding the array allocation), which is that the zero-arg and single-arg overloads return implementations that are optimized for representing empty and single-instances lists (respectively).
If we didn't have separate method overloads for these and only included a single varargs-based method, then that method would look something like this:
public static <E> ImmutableList<E> of(E... es) {
switch (es.length) {
case 0:
return emptyImmutableList();
case 1:
return singletonImmutableList(es[0]);
default:
return defaultImmutableList(es);
}
}
The performance of the switch case (or if-else checks) wouldn't be bad for most calls, but it's still unnecessary since can just have method overloads for each optimization, and the compiler always knows which overload to call. There's no burden placed on the client code, so it's an easy win.
精彩评论