首页 > 解决方案 > ClassCastException 即使在使用泛型之后

问题描述

在泛型之前

List list = new ArrayList<>();
list.add(new Manager(2));

for(Object obj : list){
   System.out.println(((Clerk)obj).display1());
}

// will lead to class cast exception.

泛型是为了避免强制转换而派生的。我编写了一些代码,即使在使用泛型之后也会给出 ClassCastException。

class Employee{
    int age;
    Employee(int age){
        this.age = age;
    }

    public void display(){
        System.out.println("age:"+age);
    }
}

class Manager extends Employee{
    int age;
    Manager(final int age) {
        super(55);
        this.age = age;
    }

    public void display(){
        System.out.println("age:"+age);
    }
}

class Clerk extends Employee{
    int age;
    Clerk(final int age) {
        super(55);
        this.age = age;
    }

    public void display1(){
        System.out.println("age:"+age);
    }
}
public static void main(String[] args) {

    List<Manager> list1 = new ArrayList<>();
    list1.add(new Manager(22));

    test(list1);

}

private static void test(List<? extends Employee> list){
    for(Employee employee1 : list){
        ((Clerk)employee1).display1();
    }
}

在该方法test中,我使用了下界泛型参数。我已经通过了作为员工子类的经理列表,但是在test我已经为另一个员工子类提供了实现,类文员导致类转换异常。

Exception in thread "main" java.lang.ClassCastException: class generics.Manager cannot be cast to class generics.Clerk (generics.Manager and generics.Clerk are in unnamed module of loader 'app')
    at generics.GenericsDemo.test(GenericsDemo.java:56)
    at generics.GenericsDemo.main(GenericsDemo.java:50)

我能够编译和执行代码意味着 Java 支持此功能,我可以在生产中使用它。如果我无法访问任何调用方方法,没有任何类,并且我需要提供测试方法的实现,那么我应该如何处理这个异常?

我应该instanceof在类型转换之前使用吗?我想从技术上了解如何处理,而不是从根本上理解它是正确还是不正确,只是如何处理异常。

标签: javagenerics

解决方案


您的代码从根本上是错误的。

您创建了一个 Manager,然后期望将其视为 Clerk。

经理不是文员,这就是它的结束。

即使列表是员工列表,一旦您创建了经理,它就是经理,当它在员工列表中时,它仍然是经理。您不能只是决定要让经理成为文员。

您的类层次结构中可能的强制转换:

  1. 经理对员工(通常是隐含的)

  2. 职员到员工(通常是隐含的)

  3. Employee 到 Manager(当且仅当对象实际上是 Manager 时

  4. Employee to Clerk(当且仅当对象实际上是 Clerk 时

要更具体地说明错误在哪里,就是这种方法:

private static void test(List<? extends Employee> list){
    for(Employee employee1 : list){
        ((Clerk)employee1).display1();
    }
}

方法声明说它可以处理员工列表或从员工继承的对象。

该方法已实现,因此如果列表包含任何实际上不是 Clerk 的 Employee,它将引发异常。

假设抛出这样的异常实际上不是“测试”方法的意图,那么实现和声明需要兼容。为什么方法没有定义为

private static void test(List<Clerk> list)

因为这就是它可以处理的全部内容,因此定义它允许在编译时验证类型。

或者,只需将他们视为员工。可以显示任何员工。

private static void test(List<? extends Employee> list){
    for (Employee employee : list) {
        employee.display();
    }
}

推荐阅读