为什么 var-arg 参数的类型“过度近似”?
如果我理解正确的话,Integer[]
是 Object[]
的子类型。例如,您可以
Object[] objs = new Integer[] { 1, 2, 3 };
在使用 var-args 时,我意识到,编译器似乎没有明显的原因“过度近似”数组类型。
例如,下面的程序打印 123
123
。如果打印 123
6
不是更有意义/更精确吗?
class Test {
public static Object combine(Object... objs) {
if (objs instanceof Integer[]) {
int sum = 0;
for (Integer i : (Integer[]) objs)
sum += i;
return sum;
} else {
String concat = "";
for (Object o : objs)
concat += o;
return concat;
}
}
public static void main(String[] args) {
System.out.println(combine("1", "2", "3")); // prints 123
System.out.println(combine(1, 2, 3)); // prints 123
}
}
我想我的问题可以总结为:如果 JLS 被定义为传递 T[]
作为参数,其中 T
是最小上限,是否会出现任何矛盾/问题给出的所有参数的类型?
编辑:我意识到,在这种特殊情况下,我也可以重载 combine
方法来获取 Integer[]
(ideone 演示)。尽管如此,问题仍然是为什么选择这种设计。
If I understand it correctly, Integer[]
is a subtype of Object[]
. You can for instance do
Object[] objs = new Integer[] { 1, 2, 3 };
While playing around with var-args I realized, that it seems like the compiler "over approixmates" the array type for no obvious reason.
The program below for instance, prints 123
123
. Wouldn't it make sense / be more precise if it printed 123
6
?
class Test {
public static Object combine(Object... objs) {
if (objs instanceof Integer[]) {
int sum = 0;
for (Integer i : (Integer[]) objs)
sum += i;
return sum;
} else {
String concat = "";
for (Object o : objs)
concat += o;
return concat;
}
}
public static void main(String[] args) {
System.out.println(combine("1", "2", "3")); // prints 123
System.out.println(combine(1, 2, 3)); // prints 123
}
}
I guess my question could be summed up as: Would any contradiction / problem arise if the JLS was defined to pass T[]
as argument, where T
was the least upper bound of the types of all arguments given?
Edit: I realize that I, in this particular case, could overload the the combine
method to take Integer[]
as well (ideone demo). Still, the question remains of why this design was chosen.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
关于这个具体问题:
是的,因为数组不是只读的;它是可写的:
则打印
如果您调用
foo(a,b,c)
, If the JLS was Defined as you are questions (eg forfoo(T... args)
) code> 然后编译器构造一个类型 a,b,c) 的最小上界的数组,那么这种情况将允许运行时错误:调用changeLastArgument(1,2,3)
会创建一个类型的数组Integer[]
,但changeLastArgument()
方法会尝试将"Pow!"
分配给最后一个元素,并且您将获得运行时错误。changeLastArgument()
的声明指定了其输入类型,因此它应该能够假设其输入参数确实是Object[]
而不是的子类型>Object[]
,以便它可以安全地修改输入参数。 (这类似于 PECS 原则 - 为了List< ;T>
为了安全地可读和可写,不能使用任何通配符,例如List
——它是安全可读但不可安全写入的-- 或List
-- 可安全写入,但不可安全读取。)As to this specific question:
Yes, because the array is not read-only; it's writable:
which prints
If the JLS was defined the way you are asking (e.g. for
foo(T... args)
, if you callfoo(a,b,c)
then the compiler constructs an array of the least upper bound of the types a,b,c), then this case would allow a runtime error: the invocation ofchangeLastArgument(1,2,3)
would create an array of typeInteger[]
, but thechangeLastArgument()
method would attempt to assign"Pow!"
to the last element and you'd get a runtime error.The declaration of
changeLastArgument()
is specifying its input types, and therefore it should be able to assume its input argument is truly anObject[]
and not a subtype ofObject[]
, so that it can safely modify the input arguments. (This is similar to the PECS principle -- in order for aList<T>
to be both safely readable and writable, you can't use any wildcards likeList<? extends T>
-- which is safely readable but not safely writable -- orList<? super T>
-- which is safely writable but not safely readable.)要打印
6
作为结果,编译器必须足够聪明才能意识到所有参数都可以装箱到类似的包装类中。我想,对于一些非常罕见的情况来说,正确指定需要太多的努力或太困难。
除了问题之外,看起来简单的规则是:数组总是类型为
Object[]
(如果可变参数类型为Object
) code>),这是一些演示代码:输出:
To print
6
as an result, the compiler would have to be clever enough to realize, that all arguments can be boxed into a similar wrapper class.I guess, this is just too much effort or too difficult to specify correctly for some very rare cases.
Besides the question, well, it looks like, the simple rule is: the array is always of type
Object[]
(if the varargs type isObject
), here's some demonstration code:Output:
在我看来,
combine(1, 2, 3)
将产生int[]
而不是Integer[]
。由于int[]
数组不是Integer[]
数组的实例,因此第一次检查失败,您将退回到 concat 块。It looks to me like
combine(1, 2, 3)
will yield aint[]
rather thanInteger[]
. Since anint[]
array is not an instance of anInteger[]
array, the first check fails, and you fall back to the concat block.JLS 指定了此行为(创建变量参数类型的元素数组,即如果 vararg 方法为
foo(Bar bar, Baz baz, T...)
那么如果您找到正确的位置,则在方法调用时创建的数组的类型为T[]
):来自 JLS 8.4.1(Oracle 站点遇到问题目前,我必须使用互联网档案馆):
从 JLS 15.12.4.2 开始:
所以我维持原来的答案(见下文)。
我相信答案就在声明中:
编译器匹配此方法,因此对于可变参数,它分配一个
Object[]
。它没有理由分配Integer[]
。试用测试:
打印:
最后,如果您希望编译器更具体,请使用通用方法:
打印:
The JLS specifies this behavior (creation of an array of elements of the type that is the variable arity parameter type, i.e. if the vararg method is
foo(Bar bar, Baz baz, T...)
then the array created on method invocation is of typeT[]
), if you find the right spot:From JLS 8.4.1 (Oracle site having trouble at the moment, I had to use the Internet Archive):
From JLS 15.12.4.2:
So I maintain my original answer (see below).
I believe the answer is in the declaration:
The compiler matches this method, and therefore for varargs it allocates an
Object[]
. There is no reason for it to allocate anInteger[]
.trial test:
which prints:
Finally, if you want the compiler to be more specific, use generic methods:
which prints:
好吧,您没有将 Integer[] 发送到组合函数中。这就是为什么它没有按您的预期工作。
使用
使其发挥作用。
Well you are not sending a Integer[] into combine function. That's why it's not working as you expect.
Use
to make it work.
我最好的猜测是您没有指定可变参数应产生的类型。
下面显示了我的意思:
输出:
很明显,通过不传递“无类型”数组,JVM 将其转换为传递给
combine()
Object[] 的对象。代码>方法。PS,由于 Oracle 服务器已关闭,我找不到 JLS。
My best guess is that you haven't specified a type that the varargs should incur.
The following shows what I mean:
Output:
It's clear that by not passing an "untyped" array, the JVM converts it to an
Object[]
that is passed to thecombine()
method.PS, I couldn't find the JLS as the Oracle Server is down.