Java 泛型帮助:无法使用“Object”作为“?”的论点扩展对象”
我有以下代码:
import java.util.*;
public class SellTransaction extends Transaction {
private Map<String,? extends Object> origValueMap;
public SellTransaction(Map<String,? extends Object> valueMap) {
super(Transaction.Type.Sell);
assignValues(valueMap);
this.origValueMap=valueMap;
}
public SellTransaction[] splitTransaction(double splitAtQuantity) {
Map<String,? extends Object> valueMapPart1=origValueMap;
valueMapPart1.put(nameMappings[3],(Object)new Double(splitAtQuantity));
Map<String,? extends Object> valueMapPart2=origValueMap;
valueMapPart2.put(nameMappings[3],((Double)origValueMap.get(nameMappings[3]))-splitAtQuantity);
return new SellTransaction[] {new SellTransaction(valueMapPart1),new SellTransaction(valueMapPart2)};
}
}
当我调用 valueMapPart1.put
和 valueMapPart2.put
时,代码无法编译,并出现错误:
The method put(String, capture#5-of ? extends Object) in the type Map is not applicable for the arguments (String, Object)
我在 Internet 上阅读了有关泛型和通配符的信息并捕获,但我仍然不明白出了什么问题。我的理解是,Map 的值可以是任何扩展 Object 的类,我认为这可能是多余的,因为所有类都扩展 Object。我无法将泛型更改为类似 的内容? super Object
,因为 Map
是由某个库提供的。
那么为什么这不能编译呢?另外,如果我尝试将 valueMap
转换为 Map
,编译器会向我发出“未经检查的转换”警告。
谢谢!
I have the following code:
import java.util.*;
public class SellTransaction extends Transaction {
private Map<String,? extends Object> origValueMap;
public SellTransaction(Map<String,? extends Object> valueMap) {
super(Transaction.Type.Sell);
assignValues(valueMap);
this.origValueMap=valueMap;
}
public SellTransaction[] splitTransaction(double splitAtQuantity) {
Map<String,? extends Object> valueMapPart1=origValueMap;
valueMapPart1.put(nameMappings[3],(Object)new Double(splitAtQuantity));
Map<String,? extends Object> valueMapPart2=origValueMap;
valueMapPart2.put(nameMappings[3],((Double)origValueMap.get(nameMappings[3]))-splitAtQuantity);
return new SellTransaction[] {new SellTransaction(valueMapPart1),new SellTransaction(valueMapPart2)};
}
}
The code fails to compile when I call valueMapPart1.put
and valueMapPart2.put
, with the error:
The method put(String, capture#5-of ? extends Object) in the type Map is not applicable for the arguments (String, Object)
I have read on the Internet about generics and wildcards and captures, but I still don't understand what is going wrong. My understanding is that the value of the Map
's can be any class that extends Object, which I think might be redundant, because all classes extend Object. And I cannot change the generics to something like ? super Object
, because the Map
is supplied by some library.
So why is this not compiling? Also, if I try to cast valueMap
to Map<String,Object>
, the compiler gives me that 'Unchecked conversion' warning.
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果库指定
extends
,那么它们明确禁止put
。您应该在修改之前进行防御性复制,因为它们可以完全合法地将其返回类型更改为在新版本中不可变。如果复制成本很高,那么您可以尝试创建
类型的地图类型,它首先查询其地图,然后查询您创建的具有本地修改的地图。如果您确实知道它们的返回类型是不可变的并且您单独拥有它,那么 @SuppressWarnings("unchecked") 注释是解决警告的合法方法,但我会仔细检查这些广泛的假设和评论。
要理解
extends
与super
,请这样看。由于该值可以是任何扩展
Object
的类型,因此以下内容是有效的。因此,
put
并不真正适用于extends
边界类型。但它与像
get
这样的读取操作完美配合:如果您希望地图主要与“put”而不是“get”一起使用,那么您可以使用“super”而不是扩展,如下所示:
If the library specifies
extends
then they are explicitly disallowingput
. You should defensively copy before modifying, since they can quite legitimately change their return type to be immutable in a new version. If copying is expensive, then you can try creating a map type that is of type<String, Object>
that first queries their map, and then queries some map you create that has your local modifications.If you do know that their return type is immutable and that you solely own it, then the
@SuppressWarnings("unchecked")
annotations is a legitimate way to work around the warning, but I would double check those assumptions and comment extensively.To understand
extends
vssuper
, look at it this way.Since the value can be any type that extends
Object
, the following is valid.So
put
doesn't really work with theextends
boundary types.But it works perfectly well with reading operations like
get
:If you want a map to primarily use with "put" instead of "get", then you can use "super" instead of extends as in:
据我所知,有界宽卡,即
? extends Number
,不用于变量或字段。它通常用于方法的参数。我们首先考虑没有泛型类型的情况。
用法示例:
您只能将
Number
的List
传递给此方法,而不能将Double
的List
传递给此方法,即使Double
是Number
的子类。此处可以使用 Widecard 泛型来告诉 java 编译器此方法可以接受
Number
的子类的任何列表。用法示例:
但是,您将无法再修改列表对象,例如
上面的列表现在具有“
Number
的未知子类型”类型,可以是List、List、List等。将Double
对象添加到未知类型列表中肯定是不安全的。为了说明这一点,对方法
的调用是对于变量和字段,您通常不使用有界宽卡,您可以这样做
注意:无需将
new Double(splitAtQuantity)
转换为其超类型,例如Number
或Object
。As far as I know, bounded widecards, i.e.
? extends Number
, is not used for variables or fileds. It is commonly used for arguments of method.Let's first consider a case without generic type.
Example usages:
You can only pass a
List
ofNumber
but not theList
ofDouble
to this method even ifDouble
is subclass ofNumber
.The widecard generic can be used here to tell java compiler that this method can accept any list of subclass of
Number
.Example usages:
However, you will no longer be able to modify the list object, e.g.
The above list now have type of "unknown subtype of
Number
" which can be List, List, List, etc. Adding aDouble
object to the list of unknown type is certainly unsafe. To illustrate this point, a call tomethod
isFor variables and fields, you normally don't use bounded widecards, you can do
Note: there is no need to cast
new Double(splitAtQuantity)
to its super type, e.gNumber
orObject
.这确实涉及到一个古老的面向对象问题。乍一看,“一袋苹果”似乎是“一袋水果”的子类,但事实并非如此。对于面向对象的代码,您始终可以使用子类代替超类(这称为 里氏替换原则)。一袋苹果就打破了这一点,因为它不接受橙子,而一袋水果会接受橙子。
就问题而言,
Collection
could 是Collection
This really goes to an old object-oriented gotcha. At first glance, it would seem that a "bag of apples" is a subclass of a "bag of fruit" but it is not. With object-oriented code, you can always use a subclass in place of a superclass (which is called the Liskov Substitution Principle). A bag of apples breaks this because it will not accept an orange whereas a bag of fruit would accept an orange.
In the terms of the question,
Collection<?>
could be aCollection<Object>
(which would accept yourDouble
) or it could be aCollection<Integer>
(which would not).