将对象强制转换为任何集合类型

5

我们最近在代码中遇到了一个与面向对象编程(OOPs)概念相关的错误。

class ABC
{
    String a;
    ABC(){
        a = "abc";
    }
}

public class Main {


    static Object listABC() {
        List<ABC> listOfABC = new ArrayList<>();
        listOfABC.add(new ABC());
        return listOfABC;
    }

    public static void main(String[] args) throws java.lang.Exception {

        List<Long> listLong = (List) Main.listABC();
        System.out.println(listLong.get(0));
    }
}

输出: ABC@642c39d2

这不应该引发运行时异常吗?有人能指导我为什么这段代码没有引发异常吗?


你现有的列表是类型为<ABC>,而你将其强制转换为LONG类型。 - Nimesh
是的,它不会引发任何运行时异常。 - Rachit Kyte.One
2
@rmagon 因为泛型在编译后就消失了。你不会得到 Runtime 异常,因为编译器会关注正确的类型。如果在运行时,某些东西是由泛型定义的不同类,则不会有异常,因为 List 的泛型类型已不存在。 - SomeJavaGuy
@ScaryWombat 不,它没有,我现在已经编辑过了。 - Rachit Kyte.One
但是如果你尝试将listLong.get(0)赋值给一个Long,你会得到一个ClassCastException。 - Scary Wombat
泛型只存在于你的IDE中。 - dieter
2个回答

7
通用类型会被擦除,在您的示例中,它们的信息在运行时不再存在。它们只是编译时的辅助工具。将 List<T> 强制转换为 List 也会在编译时丢弃有关类型的信息,尽管它仍然是有效的转换。
因此,只要每个步骤都进行合法的转换,这里就可以编译成功。在运行时,列表中的类型信息已经消失了。
如果您尝试将 get(0) 强制转换为 Long,那么您将会得到一个 ClassCastException,因为该项本身是一个 ABC。

3
实际情况更加复杂,更好的说法是在这个特定的例子中,泛型已经被去除。例如,字段或方法参数上的类型参数可以通过反射获得;如果您使用非泛型类实现泛型接口,则您使用的类型文字也可用。在Java中,类型擦除很混乱。 - Marko Topolnik
@Marko 是的。嗯,这是一个大问题。但我不知道有什么更好的方式来表达它。我会加上“在这个例子中”的说明。但是进一步的抱怨必须交给我的秘书,哈哈。 - Jason C
@MarkoTopolnik,您能否再详细解释一下?或者给我指点一些好的资源? - Rachit Kyte.One
@JasonC 我理解了你所解释的内容,但现在我对这些注释感到困惑。 - Rachit Kyte.One
3
简而言之:作为泛型类型实例的对象(在你的情况下是ArrayList),并不知道其类型参数,但在许多其他情况下也会出现泛型类型---例如在对象字段、方法参数等中。每种情况都有不同的规则,整个情况一点也不简单。 - Marko Topolnik
2
简而言之:从技术上讲,在某些情况下,类型参数的信息确实存在于运行时。只是这些信息不在正确的形式或位置,不能用于A<T>和A之间的类型转换。也就是说,“泛型类型的信息在运行时丢失”这个一般性陈述并不完全准确,因为它并不适用于你的情况。 - Jason C

3
答案很简单。泛型是编译时组件,用于确保编译时兼容性。编译后泛型就不存在了,因此在运行时不存在。
由于 Object listABC 返回一个 List,所以 (List) Test.listABC(); 不会抛出异常,因为它实际上返回了一个 List,这是有效的。
您可以将其分配给 List<Long> listLong,因为在运行时 listLong 不再知道它的 Long 泛型。
结果,即使看起来不正确,这也是有效的可执行代码。
此外,唯一能工作的原因是有一个 PrintStream#println(Object) 方法,否则您将得到一个异常,因为您试图使用错误的类型调用它,如下例所示。
static Object listABC() {
    List<Test> listOfABC = new ArrayList<>();
    listOfABC.add(new Test());
    return listOfABC;
}

public static void main(String[] args) throws java.lang.Exception {

    List<Long> listLong = (List) Test.listABC();
    System.out.println(listLong.get(0));
    test(listLong.get(0));
}

private static void test(Long longvalue) {
    System.out.println("TEST");
}

输出

ABC@19e0bfd
Exception in thread "main" java.lang.ClassCastException: ABC cannot be cast to java.lang.Long
at ABC.main(ABC.java:19)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接