使用泛型避免编译错误

Avoiding compile errors with generics

我有这个界面:

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

和这个界面:

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

现在这个class:

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 的子 class 的实例(因为 <T extends Inflatable>,对吗?),并且 readPressure 方法定义为需要 Inflatable 的子 class。

我知道这个特定示例是人为设计的,但一般情况是给定 T 的实例,然后我无法将该实例传递给出现的另一个 class 中的方法以完全相同的方式定义 T。我可以解决这个问题吗?

getPump 编辑的 Pump return 可能不是 Pump<T>。它 returns Pump<U>,其中 U 是扩展 Inflatable 的东西。假设 TU 的子类型是不安全的。

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

这就是为什么如果不违反类型安全就不能 "fix" 它。

这是一个具体示例,表明您正在尝试做错事:

   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. 
        }
    }

您唯一能做的就是重新设计界面。我无法告诉您修复它的确切方法,因为我首先不知道您的代码试图完成什么。

当你用 return 类型的 T 编写方法时,这并不意味着 returned 不能是 T 的子类。它只是意味着类型是 T就编译器而言。就像方法 return 是一个列表, returned 是一个 LinkedList 或 ArrayList 或其他什么。

当您指定 T extends something for a generic type 时,您是说编译时类型是一系列类型,它可以是 T 也可以是任何扩展 T 的类型。而且没有办法告诉类型是什么而不使用诸如 instanceof 之类的东西,然后进行转换,这违背了使用泛型的目的。

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

因此,不要对 return 类型使用带有 extends/super 的通配符。 Effective 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.

(文字在书中以粗体显示,不是我介绍的内容。)请注意,这句话是在使用有界通配符的讨论中找到的。如果您使用方法的客户真的不关心类型是什么(例如 returning a

Class<?>

从一个方法)然后继续。