Java 为什么番石榴';s的ImmutableList有这么多重载的()方法?

Java 为什么番石榴';s的ImmutableList有这么多重载的()方法?,java,guava,overloading,Java,Guava,Overloading,我刚看了番石榴,我注意到()的方法重载了12次 在我看来,他们所需要的只是: static <E> ImmutableList<E> of(); static <E> ImmutableList<E> of(E element); // not even necessary static <E> ImmutableList<E> of(E... elements); static 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);
static ImmutableList of();
(E元素)的静态不可变列表;//没必要
(E…元素)的静态不可变列表;

有这么多类似变体的原因是什么?

Varargs和泛型不能很好地结合在一起。Varargs方法可能导致带有泛型参数的警告,并且重载会阻止该警告,除非您希望使用()的
of
将11个以上的项添加到不可变列表中

消息来源中的评论说:

这些加起来是十一个。之后,您只需获取varargs表单,以及随附的任何警告:(


请注意,Java7的注释是专门添加的,以消除对这类事情的需要
可以使用带有
@SafeVarargs
注释的方法,并且不会使用泛型参数发出警告。

还有一个性能原因。每次调用varargs方法都会导致数组分配和初始化。如果您以某种方式确定,例如95%的调用包含3个或更少的参数,而只有5%的调用包含H4或更高,然后像这样重载

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 );
公共静态不可变列表();
(E)的公共静态不可变列表;
公共静态不可变列表(e1,e2);
公共静态不可变列表(e1、e2、e3);
公共静态不可变列表(e1、e2、e3、E…es);

在95%的情况下,性能得到了很好的提升。换言之,平均情况性能提高了。

除了这里的其他好答案外,还有一个微妙的运行时性能优势(除了避免阵列分配),即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);
    }
}
公共静态不可变列表(E…es){
开关(es.长度){
案例0:
返回emptyImmutableList();
案例1:
返回singletonImmutableList(es[0]);
违约:
返回默认不可变列表;
}
}

开关箱的性能(或如果有其他检查)对于大多数调用来说这不是坏事,但这仍然是不必要的,因为每次优化都可以有方法重载,而且编译器总是知道调用哪个重载。客户端代码没有负担,所以这很容易获胜。

而且它们都将参数传递给内部varargs方法……嗯。我不得不提出一些问题看了看这张。嗯,消息来源有一条评论:“这些增加到11。之后,你只需要得到varargs表单,以及任何可能伴随它而来的警告。”。不过,我不确定它指的是什么警告。@蒂姆,这可能是一个好答案,至少值得向上投票,而且可能是被接受的答案。@Romachafe我认为谷歌被击败了:尽管Groovy这样做是出于性能原因。最近在Hack News()上讨论过这一点。(“这些去11个。”--嘿,你能更新你的答案以反映@Rinke引用的性能部分吗?我认为这是值得一提的。@JoãoRebelo:但事实并非如此……这些方法只是立即去调用varargs方法本身。你的意思是在这种特定情况下吗?还是我误解了你的意思?@JoãoRe贝洛:我的意思是,对于(…)重载中的大多数
不可变列表,它们被实现为
构造(e1、e2、e3、e4)
等。其中
构造
被定义为
构造(对象…元素)
。因此无论如何都会分配一个数组。(我想这有一些好处,比如知道传递给
construct
的数组是调用方无法修改的内部数组,所以在大多数情况下,您最终不需要复制该数组,但我认为这并不特别值得关注——这当然不是创建方法的主要原因。)这很有趣。我只是通过源代码来检查这种好奇心。这是一条很好的信息,并提醒大家不要总是在假设条件下工作。注意:虽然这个原则成立,但我刚刚从ColinD那里了解到,这实际上对Guava不适用,因为重载方法无论如何都会导致varargs调用(在当前实施中)。