使用泛型避免编译错误
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
的东西。假设 T
是 U
的子类型是不安全的。
让我们假设有 2 个具体 类 实现 Inflatable
:C1
和 C2
。 getPump
可能 return Pump<C1>
的实例。假设 T
是 C2
。 C2
类型的对象不是 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<?>
从一个方法)然后继续。
我有这个界面:
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
的东西。假设 T
是 U
的子类型是不安全的。
让我们假设有 2 个具体 类 实现 Inflatable
:C1
和 C2
。 getPump
可能 return Pump<C1>
的实例。假设 T
是 C2
。 C2
类型的对象不是 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<?>
从一个方法)然后继续。