首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >在使用Java-8时使用varargs中的ClassCastException

在使用Java-8时使用varargs中的ClassCastException
EN

Stack Overflow用户
提问于 2019-12-18 06:48:23
回答 4查看 276关注 0票数 5

下面的代码对于m2()很好,但是当我使用m1()时会抛出一个ClassCastException

m1m2之间唯一的区别是参数的数量。

代码语言:javascript
运行
复制
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不适用于单个参数吗?

PS:这与ClassCastException using Generics and Varargs无关

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2019-12-18 08:15:45

现在,让我们跳过忽略未检查的强制转换警告的事实,并尝试理解为什么会发生这种情况。

在本声明中:

代码语言:javascript
运行
复制
Test.m3(Test.m4("1"));

有一种推断类型,即m4的返回类型。如果要在m3调用上下文之外使用它,如下所示:

代码语言:javascript
运行
复制
Test.m4("1"); // T is Object

T被推断为Object。可以使用类型见证强制编译器使用给定类型:

代码语言:javascript
运行
复制
Test.<String>m4("1"); // T is String

通过在赋值上下文中使用表达式来实现...or:

代码语言:javascript
运行
复制
String resString = Test.m4("1"); // T is String
Integer resInt = Test.m4("1"); // T is Integer <-- see the problem?

..。或在调用上下文中:

代码语言:javascript
运行
复制
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[],这使得您将泛型类型指定为:

代码语言:javascript
运行
复制
Test.m3(Test.<Object[]>m4("1")); // this is what is happening

现在,由于m4不返回Object[]m3正在接收String,这将导致不可避免的ClassCastException

如何解决这个问题?

修复这个问题的第一个方法是为m4指定一个正确的类型参数

代码语言:javascript
运行
复制
Test.m3(Test.<String>m4("1")); 

使用此方法,Stringm4的返回类型,m3是使用单个String对象(对于Object... var-arg)调用的,就好像您已经编写了:

代码语言:javascript
运行
复制
String temp = m4("1");
m3(temp);

第二种方法是在@Ravindra Ranwala删除的答复中提出的。在我看来,这归结为关注编译器警告:

代码语言:javascript
运行
复制
public static <T> T m4(Object s) {
    return (T) s; // unchecked cast
} 

未选中的强制转换警告只是告诉您,编译器(和运行时)不会强制类型兼容性,因为T不知道您在哪里进行转换。下面的版本是类型安全的,但它也使编译器使用String作为m4的返回类型以及m3的参数类型。

代码语言:javascript
运行
复制
public static <T> T m4(T s) {
    return s;
}

这样,m3(m4("1"));仍然使用Object...作为m3的参数类型,而将String保持为m4的返回类型(即使用字符串值作为Object数组的第一个元素)。

票数 10
EN

Stack Overflow用户

发布于 2019-12-18 07:39:10

因为在方法实现中,数组仅为read and nothing is stored in the array。但是,如果一个方法要在数组中存储一些东西,它可以尝试在数组中存储一个外来对象,比如将一个HashMap<Long,Long>放入一个HashMap<String,String>[]中。无论是编译器还是运行时系统都无法阻止它。

下面是另一个example,它说明了忽略与变量参数列表一起发出的数组构造警告的潜在危险。

代码语言:javascript
运行
复制
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[]类型

代码语言:javascript
运行
复制
        return method_2(t1, t2); 

与前面的示例一样,数组的组件类型是不可重用的,由于类型擦除,编译器不会创建T[],而是创建Object[]。下面是编译器生成的内容:

示例(上面相同的a,按类型删除翻译后):

代码语言:javascript
运行
复制
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[]

结论

在需要变量参数列表的情况下,最好避免提供不可还原类型的对象。您将始终收到未经检查的警告,除非您确切知道所调用的方法是什么,否则永远无法确保调用是类型安全的。

票数 4
EN

Stack Overflow用户

发布于 2019-12-18 08:44:42

在编译期间,由于泛型类型擦除,您必须使用T的类实例进行强制转换

代码语言:javascript
运行
复制
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();
    }
}
代码语言:javascript
运行
复制
$java Test
1
1
2
票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/59386790

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档