我有这个界面:
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}的类的方法以完全相同的方式。我能解决这个问题吗?
答案 0 :(得分:1)
Pump
返回的getPump
可能不是Pump<T>
。它返回Pump<U>
,其中U
是扩展Inflatable
的内容。假设T
是U
的子类型是不安全的。
我们假设有2个实现Inflatable
的具体类:C1
和C2
。 getPump
可能会返回Pump<C1>
的实例。我们假设T
是C2
。 C2
类型的对象不是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<?>
从一个方法)然后继续。