[英]How does java deal with generic varargs exactly?
我有一个名为Foo<T>
的通用类。
public class Foo<T> {
public void doSomething() {
Class<T> klass = T.class; // This won't compile
}
}
经过一番谷歌搜索,我发现由于java通过执行类型擦除来实现泛型,因此它不知道T
的运行时类型并且拒绝编译。 但是,如果我尝试这个......
public class Foo<T> {
public void doSomething(T... ts) {
Class<T> klass = (Class<T>) ts.getClass().getComponentType();
System.out.println(klass);
}
}
...并运行以下代码...
Foo<String> foo1 = new Foo<>();
foo1.doSomething("foo", "bar"); // class java.lang.String
foo1.doSomething(); // class java.lang.String
Foo foo2 = foo1;
foo2.doSomething("foo", "bar"); // class java.lang.Object
foo2.doSomething(); // class java.lang.Object
Foo<? super String> foo3 = foo1;
foo3.doSomething("foo", "bar"); // class java.lang.Object
foo3.doSomething(); // class java.lang.Object
第一个打印class java.lang.String
而其他两个打印class java.lang.Object
,这对我来说没有多大意义 - 对象的行为如何取决于它分配给的变量类型? 另外,如果java做了类型擦除,它又怎么知道第一种情况下类型参数实际上是java.lang.String
呢? 而且,由于我无法将Object
分配给T
,java 如何将Object[]
分配给T[]
? 似乎它破坏了类型安全!
我想 java 在这里隐式地传递了运行时类,因为它不能从参数中推断出来,因为在第二个方法调用中根本没有参数。 我想知道 java 在幕后做了什么,如果有人能告诉我更多关于通用可变参数的信息,我会很高兴。
我是 StackOverflow 的新手。 如果我违反了一些规则,请告诉我:)
首先,可变参数只是数组参数的语法糖。 所以:
clazz.varArgsMethod("A", "B");
与(并且可以作为)相同:
clazz.varArgsMethod(new SomeElementType[]{"A", "B"});
其中SomeElementType
由编译器确定。
在呼叫站点:
Foo<String> foo1 = new Foo<>();
foo1.doSomething("foo", "bar");
foo1.doSomething();
编译器知道doSomething
是一个可变参数方法,并且需要一个T[]
作为可变参数参数。 因为它知道T
是String
为一个Foo<String>
,它知道T
必须是String
。 所以它将这段代码编译为:
Foo<String> foo1 = new Foo<>();
foo1.doSomething(new String[]{"foo", "bar"});
foo1.doSomething(new String[]{});
String[]
数组类型具有String
组件类型。 该方法传递了String[]
实例,可以反射性地查询它们的组件类型。
其他两种情况基本相同: T
由Foo
的类型确定:
Foo
, T
被擦除。 T
的擦除是Object
Foo<? super String>
Foo<? super String>
, T
的上界为Object
因此,它创建并传递一个Object[]
。
...而且,由于我无法将 Object 分配给 T,java 如何将 Object[] 分配给 T[]? 似乎它破坏了类型安全!
是的,你是对的。 泛型参数类型的可变参数实际上确实违反了 Java 自己的类型安全规则。
为什么?
因为数组是协变的(和具体化的)而泛型是不变的(被擦除的,或非具体化的)。 用 Joshua Bloch 的话来说,“数组和泛型不能很好地结合在一起。” Java 语言的开发人员做出了一个非常深思熟虑的决定来实现 varargs 语言功能,尽管它违反了类型安全。 他们得出结论,拥有可变参数(被反射和注释工具广泛使用)的价值证明了它们的风险和局限性。
在 Java 中,您不能编译此代码:
T[] genericArray = new T[SIZE];
这是非法的,因为根据 Java 自己的类型安全规则,实例化非具体类型的数组不是类型安全的。 然而,当客户端调用具有泛型类型的 varargs 参数的方法时,运行时系统将实例化该泛型类型的数组! 它不是类型安全的!
如果不小心使用可变参数系统,程序中可能会出现ClassCastException
或其他错误。 本质上,开发人员和运行时系统之间隐含了一个不可执行的合同:
由通用 varargs 参数产生的数组...
换句话说,开发人员承诺仅将生成的通用数组用作完成计算的可变数量的参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.