以下代码对于 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();
}
}
我的问题是 - 当我们使用泛型时,可变参数不适用于单个参数吗?
最佳答案
让我们暂时跳过您忽略了未经检查的转换警告的事实,并尝试理解为什么会发生这种情况。
在此声明中:
Test.m3(Test.m4("1"));
有一种推断类型,即m4
的返回类型。如果要在 m3
调用上下文之外使用它,如下所示:
Test.m4("1"); // T is Object
T
被推断为 Object
。可以使用类型见证来强制编译器使用给定类型:
Test.<String>m4("1"); // T is String
...或者通过在赋值上下文中使用表达式:
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[]
。这意味着 必须与 m3
的参数类型一致的 T
因此被解析为 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
的返回类型,并且使用单个 String
调用 m3
对象(对于 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
数组的第一个元素)。
关于java - 使用 Java-8 时可变参数中的 ClassCastException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59386790/