使用 vararg 的方法重载

问题描述 投票:0回答:1

当设计具有可变参数长度的(高性能)方法时,例如

<T> void consume(T t1)
<T> void consume(T t1, T t2)
<T> void consume(T t1, T t2, T t3)

a) 最后带有 vararg 的另一个重载,例如ImmutableList.of()

<T> void consume(T t1, T t2, T t3, T... others)

b) 另一个带有单个 vararg 参数的重载,例如List.of()

<T> void consume(T... tees)

每个 vararg 变体的优点和缺点是什么?

java overloading variadic-functions
1个回答
0
投票

void consume(T... tees)
的问题在于它都是语法糖。这个:

void consume(T... tees) {
  ....
}

consume(a, b);

这是编译为:

void consume(T... tees) {
  ....
}

consume(new Object[] {a, b});

“成本”是创建该 2 元素数组。这并不是一个很高的成本,而且一般来说“性能”根本不是你可以这样推理的:Hotspot 比你想象的要复杂得多,所以,在你之前你真的需要一份分析器报告只是盲目地假设制作 2-args 数组实际上是一个性能问题。

在到达可变参数之前,拥有一堆参数数量不断增加的方法的目的是避免创建数组。然而,你所展现的风格有点错误。这是对的:

void consume(T a) { ??? }
void consume(T a, T b) { ??? }
void consume(T a, T b, T c, T... rest) { ??? }

换句话说,你不断地添加一个参数,一旦你觉得已经足够了,最后一个就会得到

T... rest
。您不需要同时使用
consume(T a, T b,  T c)
(T a, T b, T c, T... rest)
,因为这意味着
consume(x, y, z)
形式的调用是不明确的(您是用零“其余”参数调用第一个还是第二个,这是有效的java?)

那么什么时候你会“停止”并使用 varargs 呢?好吧,你告诉我:当尝试节省数组分配的意义不再值得做时; “如果你无论如何都要传递大量的参数,性能可能会超出预期”和“向此方法传递 12 个参数是极其罕见的,因此不值得尝试优化”的某种组合。

一般来说,你应该只编写一种方法(

consume(T... tees)
),而不用担心所有这些重载。 JVM“非常擅长”优化常见模式,而可变参数是一种非常常见的模式。重载维护起来很烦人,更难记录,而且还增加了 API 的烦恼 - 如果我碰巧有一个 T 元素数组,我现在不能再调用你的 API。

© www.soinside.com 2019 - 2024. All rights reserved.