下面的代码对于m2()
很好,但是当我使用m1()
时会抛出一个ClassCastException
。
m1
和m2
之间唯一的区别是参数的数量。
public class Test {
public static void m1() {
m3(m4("1"));
}
public static void m2() {
m3(m4("1"), m4("2"));
}
public static void m3(Object... str) {
for (Object o : str) {
System.out.println(o);
}
}
public static <T> T m4(Object s) {
return (T) s;
}
public static void main(String[] args) {
m1();
}
}
我的问题是,当我们使用泛型时,varargs不适用于单个参数吗?
发布于 2019-12-18 08:15:45
现在,让我们跳过忽略未检查的强制转换警告的事实,并尝试理解为什么会发生这种情况。
在本声明中:
Test.m3(Test.m4("1"));
有一种推断类型,即m4
的返回类型。如果要在m3
调用上下文之外使用它,如下所示:
Test.m4("1"); // T is Object
T
被推断为Object
。可以使用类型见证强制编译器使用给定类型:
Test.<String>m4("1"); // T is String
通过在赋值上下文中使用表达式来实现...or:
String resString = Test.m4("1"); // T is String
Integer resInt = Test.m4("1"); // T is Integer <-- see the problem?
..。或在调用上下文中:
Integer.parseInt(Test.m4("1")); // T is String
Long.toString(Test.m4("1")); // T is Long
现在,回到Test.m3(Test.m4("1"));
:我找不到这方面的引用,但我认为编译器不得不将T
解析为m3
的参数类型,即Object[]
。这意味着T
(必须与m3
的参数类型重合)因此被解析为Object[]
,这使得您将泛型类型指定为:
Test.m3(Test.<Object[]>m4("1")); // this is what is happening
现在,由于m4
不返回Object[]
,m3
正在接收String
,这将导致不可避免的ClassCastException
。
如何解决这个问题?
修复这个问题的第一个方法是为m4
指定一个正确的类型参数
Test.m3(Test.<String>m4("1"));
使用此方法,String
是m4
的返回类型,m3
是使用单个String
对象(对于Object...
var-arg)调用的,就好像您已经编写了:
String temp = m4("1");
m3(temp);
第二种方法是在@Ravindra Ranwala删除的答复中提出的。在我看来,这归结为关注编译器警告:
public static <T> T m4(Object s) {
return (T) s; // unchecked cast
}
未选中的强制转换警告只是告诉您,编译器(和运行时)不会强制类型兼容性,因为T
不知道您在哪里进行转换。下面的版本是类型安全的,但它也使编译器使用String
作为m4
的返回类型以及m3
的参数类型。
public static <T> T m4(T s) {
return s;
}
这样,m3(m4("1"));
仍然使用Object...
作为m3
的参数类型,而将String
保持为m4
的返回类型(即使用字符串值作为Object
数组的第一个元素)。
发布于 2019-12-18 07:39:10
因为在方法实现中,数组仅为read and nothing is stored in the array
。但是,如果一个方法要在数组中存储一些东西,它可以尝试在数组中存储一个外来对象,比如将一个HashMap<Long,Long>
放入一个HashMap<String,String>[]
中。无论是编译器还是运行时系统都无法阻止它。
下面是另一个example
,它说明了忽略与变量参数列表一起发出的数组构造警告的潜在危险。
static <T> T[] method_1(T t1, T t2) {
return method_2(t1, t2); // unchecked warning
}
static <T> T[] method_2( T... args) {
return args;
}
public static void main(String... args) {
String[] strings = method_1("bad", "karma"); // ClassCastException
}
警告:未选中的、未检查的泛型数组为varargs参数创建T[]类型
return method_2(t1, t2);
与前面的示例一样,数组的组件类型是不可重用的,由于类型擦除,编译器不会创建T[],而是创建Object[]。下面是编译器生成的内容:
示例(上面相同的a,按类型删除翻译后):
public final class Test {
static Object[] method_1( Object t1, Object t2) {
return method_2( new Object[] {t1, t2} ); // unchecked warning
}
static Object[] method_2( Object[] args) {
return args;
}
public static void main(String[] args) {
String[] strings = (String[]) method_1("bad", "karma"); // ClassCastException
}
}
发出未经检查的警告,以提醒您注意类型安全违规和意外ClassCastExceptions的潜在风险。
在本例中,您将在main()
方法中观察到一个main()
,其中两个字符串被传递给第一个方法。在运行时,这两个字符串被填充到一个Object[]
中;注意,not a String[]
。
第二个方法接受Object[] as an argument
,因为在类型擦除之后,Object[]
是它声明的参数类型。因此,第二个方法返回一个Object[] , not a String[]
,它作为第一个方法的返回值传递。最后,main()
方法中的编译器生成的强制转换失败,because the return value of the first method is an Object[] and no String[]
结论
在需要变量参数列表的情况下,最好避免提供不可还原类型的对象。您将始终收到未经检查的警告,除非您确切知道所调用的方法是什么,否则永远无法确保调用是类型安全的。
发布于 2019-12-18 08:44:42
在编译期间,由于泛型类型擦除,您必须使用T的类实例进行强制转换
public class Test {
public static void m1() {
m3(m4("1", String.class));
}
public static void m2() {
m3(m4("1", String.class), m4("2", String.class));
}
public static void m3(final Object... str) {
for (Object o : str) {
System.out.println(o);
}
}
public static <T> T m4(final Object s, Class<T> clazz) {
return clazz.cast(s);
}
public static void main(String[] args) {
m1();
m2();
}
}
$java Test
1
1
2
https://stackoverflow.com/questions/59386790
复制相似问题