首页 > 解决方案 > 使用 Java-8 时 varargs 中的 ClassCastException

问题描述

以下代码适用于m2()ClassCastException在我使用m1().

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();
   }
 }

我的问题是 - 当我们使用泛型时,可变参数不能与单个参数一起使用吗?

PS :这与使用泛型和可变参数的 ClassCastException无关

标签: javagenericsexceptionjava-8

解决方案


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

在本声明中:

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[]. 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数组的第一个元素)。


推荐阅读