下面的代码对于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 ObjectT被推断为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
2https://stackoverflow.com/questions/59386790
复制相似问题