用泛型避免不安全的铸造

时间:2013-10-13 05:48:23

标签: java generics

假设我有一个抽象类:

public abstract class Trainer<T extends Animal>{
...
}

我希望所有的儿童课程都有一个通用的方法来检查教练对某一动物的训练状态。所以我的培训师定义:

public abstract class Trainer<T extends Animal>{
   public double getStatus(Animal a){
   ...
   }
}

这样任何人都可以查询培训师,无论其具体类型如何。但是,为了让各个培训师负责报告自己的状态,我要求各个培训师实施internalGetStatus方法,然后我会按照getStatus方法调用。所以我现在正在做的是:

public abstract class Trainer<T extends Animal>{
   protected abstract internalGetStatus(T theAnimal);
   public double getStatus(Animal a){
     return internalGetStatus((T) a);
   }
}

问题是,当然,return internalGetStatus((T) a);涉及一个未经检查的类型转换,它会抛出我想要避免的警告。

有办法吗?

由于我无法控制的某些设计限制,当查询特定动物的状态时,它将作为父类Animal的对象提供,而不是为其创建训练器的特定动物类型。

1 个答案:

答案 0 :(得分:3)

这取决于类的使用方式。但是,你没有多说这些。让我们从以下代码开始:

abstract class Animal { }

final class Lion extends Animal { }

abstract class Trainer<T extends Animal> {
    public abstract double getStatus(T animal);
}

final class LionTrainer extends Trainer<Lion> {
    public double getStatus(Lion lion) {
        return 4.2;
    }
}

您提到getStatus方法的呼叫方不知道动物类型。这意味着他没有使用Trainer<Lion>,而是原始类型Trainer<Animal>通配符类型。让我们来看看这三种情况。

原始类型:类型参数对原始类型无关紧要。您可以使用getStatus调用方法Animal。只要TrainerAnimal的子类型匹配,此功能就可以使用。否则,您将在运行时看到ClassCastException

void raw(Trainer trainer, Animal animal) {
    trainer.getStatus(animal);
}

Trainer<Animal>: 显然,您可以使用getStatus调用Trainer<Animal>的方法Animal。它类似于上述情况。静态类型系统不存在,并且在运行时,如果类型不匹配,您将看到异常。

void base(Trainer<Animal> trainer, Animal animal) {
    trainer.getStatus(animal);
}

请注意,您可以将LionTrainer传递给此方法(使用强制转换),因为在运行时,Trainer<Animal>Trainer<Lion>之间没有区别。

通配符类型:嗯,这不适用于来电方 - 没有将Trainer<?>转换为其他内容。 ?代表Animal的未知子类型(包括基类本身)。

void wildcard(Trainer<?> trainer, Animal animal) {
    trainer.getStatus(animal); // ERROR
}

结果是,如果框架使用原始类型基类型,那么应该没有问题。否则,将转换添加到代码中更简单,使用注释抑制警告,并记录您决定采用这种方式的原因。