Почему у Guava ImmutableList так много перегруженных методов()?
Я просто смотрел на Guava ImmutableList
, и я заметил, что метод of()
был перегружен 12 раз.
Мне кажется, что все, что им нужно, это:
static <E> ImmutableList<E> of();
static <E> ImmutableList<E> of(E element); // not even necessary
static <E> ImmutableList<E> of(E... elements);
В чем причина стольких подобных изменений?
Ответы
Ответ 1
Варгары и дженерики не играют хорошо вместе. Методы Varargs могут вызывать предупреждение с помощью общих аргументов, а перегрузки предотвращают это предупреждение, за исключением редкого случая, когда вы хотите добавить более 11 элементов в неизменяемый список, используя of()
.
Комментарии в источнике говорят:
Это до одиннадцати. После этого вы просто получите форму varargs, и любые предупреждения могут появиться вместе с ней.: (
Обратите внимание, что аннотация Java 7 @SafeVarargs была добавлена специально для устранения необходимости в подобных вещах. Один метод of(E...)
, аннотированный с помощью @SafeVarargs
, может использоваться и не будет давать предупреждения с общими аргументами.
Ответ 2
Там также причина производительности. Каждый вызов метода varargs вызывает распределение и инициализацию массива. Если вы каким-то образом определили, что, например, 95% вызовов имеют 3 или менее аргумента и только 5% с 4 или более, а затем перегрузка, как эта
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 );
приводит к хорошему повышению производительности в 95% случаев. Иными словами, средняя производительность приложения увеличивается.
Ответ 3
В дополнение к другим замечательным ответам здесь есть тонкое преимущество производительности во время выполнения (в дополнение к исключению выделения массива), которое заключается в том, что возвратные реализации zero-arg и single-arg возвращают реализации, которые оптимизированы для представления пустых и одиночных -инструменты (соответственно).
Если бы у нас не было отдельных перегрузок методов для них и включался только один метод на основе varargs, тогда этот метод выглядел бы примерно так:
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);
}
}
Производительность коммутатора (или проверки if-else) не будет плохой для большинства вызовов, но она по-прежнему не нужна, поскольку может просто перегружать метод для каждой оптимизации, а компилятор всегда знает, какую перегрузку нужно вызывать. На клиентский код нет бремени, так что это легко победить.