避免编译泛型错误

问题描述:

我有这样的接口:避免编译泛型错误

public interface Inflatable { 
    Pump<? extends Inflatable> getPump(); 
} 

该接口:

public Pump<T extends Inflatable> { 
    int readPressure(T thingToInflate); 
} 

现在这个类:

public class Preparer { 
    public <T extends Inflatable> void inflate(T thingToInflate) { 

     int pressure = thingToInflate.getPump().readPressure(thingToInflate); 
    } 
} 

不能编译,与此错误:

The method readPressure(capture#1-of ? extends Inflatable) in the type Pump is not applicable for the arguments (T)

这里有什么问题?变量thingToInflate必须是Inflatable(因为<T extends Inflatable>,因为?)子类的实例,并且readPressure方法被定义为需要Inflatable的子类。

我知道这个特定的例子是人为的,但一般情况是,给定的T一个实例,我不能然后传递该实例的方法,在出现在完全相同的方式来定义T另一个类。我能解决这个问题吗?

+0

从给出的例子看来,'Pump'实际上不应该是通用的,因为你期望的用法表明'int readPressure(Inflatable)'。可能还会看到[PECS](https://*.com/q/2723397/2891664)。 – Radiodef

getPump返回的Pump可能不是Pump<T>。它返回Pump<U>,其中U是延伸Inflatable的东西。假设TU的子类型并不安全。

我们假设有2个具体类实现InflatableC1C2getPump可能会返回Pump<C1>的实例。我们假设TC2C2类型的对象不是C1的实例,因此它不能传递给readPressure方法。

这就是为什么没有类型安全违规就无法“修复”它的原因。

这里是表明你正在尝试做错误的事情一个具体的例子:

class C1 implements Inflatable, Pump<C1> { 
     @Override 
     public Pump<? extends Inflatable> getPump() { 
      return this; // an instance of C1 which implements Pump<C1> 
     } 

     @Override 
     public int readPressure(C1 thingToInflate) { 
      return 0; 
     } 
    } 

    class C2 implements Inflatable { 
     @Override 
     public Pump<? extends Inflatable> getPump() { 
      return new C1(); // again, an instance of C1 which implements Pump<C1> 
     } 
    } 

    public class Preparer { 
     public <T extends Inflatable> void inflate(T thingToInflate) { 
      int pressure = thingToInflate.getPump().readPressure(thingToInflate); 
      // Let's assume that it were possible. What happens if one calls 
      // new Preparer().inflate(new C2())? 
      // new C2().getPump() returns an instance of C1 which implements Pump<C1> 
      // It's readPressure method expects an instance of C1. But T = C2, so 
      // the thingToInflate is not an instance of C1. 
      // If the compiler allowed this to happen, the type safety 
      // would be violated. 
     } 
    } 

你能做的唯一的事情就是重新设计的接口。我不能告诉你一个确切的方法来解决它,因为我不知道你的代码试图完成什么。

当您编写一个返回类型为T的方法时,这并不意味着返回的事物不能是T的子类。它只是意味着类型是T就编译器而言。就好像该方法返回一个List,返回的是一个LinkedList或ArrayList或其他。

当你指定T为泛型类型扩展某些东西时,你会说编译时类型是一个类型范围,它可能是T或者它可能是任何扩展T的类型。并且没有办法知道什么该类型不使用instanceof之类的东西,然后进行投射,从而击败了使用泛型的目的。

指定一系列类型对方法参数有意义。如果我有一个方法需要T extends Animal的列表,则调用者可以传入狗列表或猫列表。在其他情况下,这没有帮助。

所以不要使用扩展/超类型的通配符作为返回类型。有有效的Java(第5章,第28项)说这句话的条目,引用:

Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code.

(该文本是因为它出现在书中粗体,它是不是我介绍了。)请注意,这句话在使用有界通配符的讨论中找到。如果你的客户使用的方法真的不在乎什么类型(例如从方法返回

Class<?> 

)然后继续。