【问题标题】:Avoiding unsafe casting with generics避免使用泛型进行不安全的强制转换
【发布时间】:2013-10-13 18:43:17
【问题描述】:

假设我有一个抽象类:

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 的对象提供,而不是为其创建训练器的特定动物类型。

【问题讨论】:

  • 为什么 getStatus 取 Animal ?为什么不接受 T ?只要定义了 T 的基数,将其转换为 T 就没有错。
  • @anishthecoder 为什么你不能将'public double getStatus(Animal a)'方法更改为'public double getStatus(T a)'?
  • @DevBlanked @HarshaR,由于我无法控制的一些设计限制,我的 getStatus 方法只能提供 Animal 而不能提供 T....
  • @anishthecoder 可能你可以使用 'getActualTypeArguments()' 方法 docs.oracle.com/javase/6/docs/api/java/lang/reflect/… 然后在类上使用 'isInstance' 方法来确定 'a' 是否实际上是 'T' 类型
  • 好吧,如果它明确地给你一个 Animal 值,那也不能工作,因为如果实例实际上不是 Lion,则将 Animal 转换为 Lion 将返回 null。如果它确实是 Lion,您可以使该方法成为通用方法并从类外部添加强制转换:它将放置在最容易理解的位置:不要隐藏它以防有人破坏这个静默 API。

标签: java generics


【解决方案1】:

这取决于类的使用方式。不过,你对此并没有多说。让我们从以下代码开始:

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&lt;Lion&gt;,而是原始类型Trainer&lt;Animal&gt;通配符类型。让我们来看看这三个案例。

原始类型: type 参数与原始类型无关。您可以使用Animal 调用方法getStatus。只要TrainerAnimal 的子类型匹配,它就可以工作。否则,您将在运行时看到ClassCastException

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

Trainer&lt;Animal&gt;: 显然,您可以使用Animal 调用Trainer&lt;Animal&gt; 的方法getStatus。与上述情况类似。静态类型系统不知道,如果类型不匹配,在运行时你会看到一个异常。

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

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

通配符类型: 好吧,这在调用方不起作用 - 没有将 Trainer&lt;?&gt; 转换为其他东西。 ? 代表 Animal 的未知子类型(包括基类本身)。

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多