使用泛型避免编译错误

时间:2017-08-10 18:39:14

标签: java generics type-erasure

我有这个界面:

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);
    }
}

无法编译,出现此错误:

  

类型Pump中的readPressure方法(捕获#1-of?extends Inflatable)不适用于参数(T)

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

我知道这个特定的例子是人为的,但是一般的情况是,给定Inflatable的实例,我不能将该实例传递给另一个看似定义{{1}的类的方法以完全相同的方式。我能解决这个问题吗?

2 个答案:

答案 0 :(得分:1)

Pump返回的getPump可能不是Pump<T>。它返回Pump<U>,其中U是扩展Inflatable的内容。假设TU的子类型是不安全的。

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

这就是为什么一个人无法修复&#34;没有类型安全违规。

以下是一个具体的例子,表明你正在尝试做错事:

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

您唯一能做的就是重新设计界面。我无法告诉您一个确切的解决方法,因为我不知道您的代码首先想要完成什么。

答案 1 :(得分:1)

当你编写一个返回类型为T的方法时,这并不意味着返回的东西不能成为T的子类。就编译器而言,它只是意味着类型是T 。就像方法返回List一样,返回的东西是LinkedList或ArrayList或者其他什么。

当你为泛型类型指定T扩展时,你会说编译时类型是一系列类型,它可以是T或它可以是任何类型扩展T.并且有&#39; s如果不使用像instanceof这样的东西,然后进行强制转换,就无法告诉使用泛型的目的。

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

所以不要使用带有extends / super的通配符作为返回类型。在Effective Java(第5章,第28项)中有一个条目说这个,引用:

  

不要将通配符类型用作返回类型。   它不会为用户提供额外的灵活性,而是迫使他们在客户端代码中使用通配符类型。

(文本在书中出现时加粗,这不是我介绍的内容。)请注意,这个引用是在使用有界通配符的讨论中找到的。如果您的客户使用某种方法真的不关心类型是什么(例如返回

Class<?>

从一个方法)然后继续。