方法参数的通用类型转换不可行,为什么?

3

我是新手,对泛型不太熟悉。在尝试在一个小程序中使用它们时,遇到了以下问题。下面是必要的信息。

我会尽量提供必要的信息,如果有任何缺失,请告诉我,我会提供所需的内容。

首先是以下接口:

1: public interface IPrice<T extends IPrice<T>> {
2:     void setPriceToZero();
3:     T addPrice(T price);
4:     T subtractPrice(T price);
5:     T multiplyPriceByFactor(int factor);
6:     String priceToString();
7: }

这是由类BnSPrice实现的;以下是可能需要的部分:

1: public class BnSPrice implements IPrice<BnSPrice> {
2:     public BnSPrice subtractPrice(BnSPrice price) {...}
2: }

现在这部分不能编译,而且我也不太理解:

1: public static void main(String[] args){
2:     [...]
3:     IPrice<? extends BnSPrice> test = new BnSPrice(5);
4:     test.subtractPrice(new BnSPrice(5));
5:     [...]
6: }

我的问题是

"错误:(27,28)java:不兼容的类型:main.bnsCalculatorModel.BnSPrice 无法转换为捕获#1 of ? extends main.bnsCalculatorModel.BnSPrice"

问题出现在我调用减法方法的那一行。

为什么会出现这种情况,我该如何更正,以使我仍然可以通过接口编程?

(我的目标是,将来可以轻松地通过其他“价格类型”扩展我的程序。)


请添加行号!错误在于 IPrice<? extends BnSPrice> test = new BnSPrice(5); 这一行吗? - Pyranja
可能是重复的问题:为什么泛型类型不能同时适用于“extends超类”和“super超类”的参数?(原文链接:https://dev59.com/NmEi5IYBdhLWcg3wPadh) - Julien Lopez
添加行号:错误在代码块3的第4行。Sweeper做了很好的解释。 - Wolfone
2个回答

6

首先,让我们看看如何使其工作:

IPrice<BnSPrice> test = new BnSPrice(5);
test.subtractPrice(new BnSPrice(5));

好的,所以移除通配符? extends ...是可行的。为什么?

您需要了解泛型通配符的概念。<? extends BnPrice>表示可以是任何类型,该类型是BnPrice的子类。目前,您没有任何子类,但让我们创建一个子类来演示我的观点。

class MyPrice extends BnPrice {}

请注意,您不能将BnPrice对象分配给类型为MyPrice的变量。
太好了!现在我们有了我们的test对象。让我们看看subtractPrice在类型IPrice<? extends BnPrice>中是如何定义的:
T subtractPrice(T price);

由于您编写了<? extends BnPrice>T可以是BnPrice的任何子类,例如BnPriceMyPrice。如果TBnPrice,那么这将被编译:

test.subtractPrice(new BnSPrice(5));

你正在将一个 BnPrice 传递给一个期望接收 BnPrice 的方法。一切正常。但是,T 不一定要是 BnPrice。它也可以是 MyPrice!如果是 MyPrice,那么上面的代码行不会编译,因为你不能将 BnPrice 传递给 MyPrice 参数。
由于参数可以是扩展 BnPrice 的任何类型,因此您无法将 BnPrice 传递给它。

好的,对我来说在某种程度上是有道理的;至少你提到编译器不知道你示例中的类型是MyPrice还是BnSPrice这一点是有道理的;有点难以理解为什么我们通过接口所做的契约不足够,但我模糊地认为它只在我的特殊情况下足够,而不是普遍适用的;可能是因为我尝试调用一个子类型中存在但另一个子类型中不存在的方法。我想得对吗?哦,还有一个跟进问题:你的第一个代码块是我可以得到的最通用版本吗? - Wolfone
@Wolfone 我认为你的思路是正确的,而且就我而言,那几乎是你能得到的最普遍的版本。 - Sweeper

0
因为测试的类型是IPrice<? extends BnSPrice>,所以subtract的参数的预期类型是? extends BnSPrice,而不是BnSPrice。

这将编译:

BnSPrice test = new BnSPrice(5);
test.subtractPrice(new BnSPrice(5));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接