使用可变参数时,结果数组的组件类型是如何确定的?如何确定可变参数阵列的组件类型?
例如,此程序是否保证打印true
或者是其技术上未指定的行为?
public static void main(String[] args) {
foo("", 0);
}
static <T> void foo(T... arr) {
System.out.println(arr.getClass() == Serializable[].class);
}
使用可变参数时,结果数组的组件类型是如何确定的?如何确定可变参数阵列的组件类型?
例如,此程序是否保证打印true
或者是其技术上未指定的行为?
public static void main(String[] args) {
foo("", 0);
}
static <T> void foo(T... arr) {
System.out.println(arr.getClass() == Serializable[].class);
}
嘛,我不是100%肯定这一点,但我认为这无关可变参数,而更像是动态绑定,泛型和类型擦除。
“Java中的数组是协变的,但泛型不是。换句话说,String []是Object []的子类型,但Stack不是Stack的子类型。
来源: http://algs4.cs.princeton.edu/13stacks/
因此,要回答你的问题,这种行为在指定记录和预期。
在这本书:“有效的Java”约书亚·布洛克解释它是这样的:
阵列从泛型类型在两个重要方面有所不同。首先,数组是协变的。这个可怕的单词意味着如果Sub是Super的子类型,那么数组类型Sub []是Super []的子类型。相比之下,泛型是不变的:对于任何两种不同类型Type1和Type2,列表<Type1>既不是子类型也不是超类型List <Type2> [JLS,4.10; Naftalin07,2.5]。你可能会认为这意味着泛型是有缺陷的,但可以说它是缺陷的数组。
为什么我在谈论你可能会问的数组?那么,因为可变参数最终会变成数组。 “
”在过去的版本中,采用任意数量值的方法需要您在调用方法之前创建数组并将其放入数组。
“多个参数必须在数组中传递,但可变参数功能自动化并隐藏进程仍是事实。”
来源: https://docs.oracle.com/javase/8/docs/technotes/guides/language/varargs.html
这个答案可能不是100%你正在寻找的答案,但也许它帮助。一般来说,arr.getClass()将返回一些数组类(最常用的Object[].class
,但也可能是Integer[].class
,Number[].class
或Serializable[].class
-通常是所有元素中最具体的类型,但我不会指望它会看到m- szalik答案)。如果你想确保数组中包含的所有类都是Serializable
的实例,则必须检查每个元素(btw。所提出的实现不支持null
值):
static <T> void foo(T... arr) {
System.out.println(Stream.of(arr)
.filter(e -> !Serializable.class.isInstance(e.getClass()))
.findFirst()
.orElse(null) == null);
}
你可能想看看:
顺便说一句。我同意Fogetti船长的意见:
嘛,我不是100%肯定这一点,但我认为这无关可变参数,而更像是动态绑定,泛型和类型擦除。
注意:
只要你foo
实现的一些例子:
foo(1, 2)
将Integer[].class
foo(1, 2.0)
是Number[].class
foo ("", 1)
将Serializable[].class
foo(null, 2)
将Integer[].class
foo("", new Object())
将Object[].class
这是不是可变参数,但更多的仿制药。
通用信息在编译过程中丢失。您的varargs参数由编译器转换为数组。所以JVM不会决定类型,但编译器会这样做。
你的代码被编译成字节码为folows:
public static void main(String[] args) {
foo(new Serializable[]{"", Integer.valueOf(0)});
}
决定的算法是相当长的,但你可以在这里读到它。 https://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html
我跑这个代码和输出告诉你有没有保证(至少如果类有不同层次分支机构超过一个共同的祖先)
老实说,我不知道这个魔术背后的原因,但我就是无法发布它作为一个评论
import java.util.*;
import java.lang.*;
import java.io.*;
class Ideone
{
interface A{}
interface B{}
class AB implements A, B {}
class BA implements A, B {}
public static void main (String[] args) throws java.lang.Exception
{
foo(new AB(), new BA());
foo2(new AB(), new BA());
}
static <T> void foo(T... arr) {
System.out.println(arr.getClass() == A[].class);
}
static <T> void foo2(T... arr) {
System.out.println(arr.getClass() == B[].class);
}
}
输出
true
false
更奇怪的事情:
如果interface B
interface A
之前声明,结果是相反的:
false
true
在方法调用,方法声明的顺序和接口秩序implements
块更改的参数顺序会对我没有影响(1.8.0_51)。
我期望这里的答案是“所有类型推理规则在它们难以捉摸的复杂性中总是至少与我尝试过的每个编译器有一些不匹配。” –
可能是:http://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.5.1及以下。 – assylias