当我将对象添加到不可变地图时,我注意到了这一点。
如果我添加一个像这样的对象:
Map<String, Object> model = ImmutableMap.of(
"post", post
);
我收到编译时错误,说帖子不是对象。
如果我这样做:
Map<String, Object> model = ImmutableMap.of(
"post", post,
"asdf", new Object()
);
编译好。有没有办法让post对象转换为一个对象,以便它可以使用单个对象?
或者更重要的是,为什么会发生这种情况?
答案 0 :(得分:2)
我认为问题在于类型推断。
在第二个示例中,您使用的是声明为
的of(..)
方法
public static <K, V> ImmutableMap<K, V> of(K k1, V v1, K k2, V v2) {
在这种情况下,编译器将尝试根据方法的用法推断类型参数绑定到类型变量V
。在你的情况下,那是
Map<String, Object> model = ImmutableMap.of(
"post", post,
"asdf", new Object()
);
其中post
和new Object()
是将被检查以推断该类型参数的表达式。编译器将使用引用类型中最低的共同祖先。如果类型不相关,那显然是Object
。所以V
绑定类型Object
,方法返回类型匹配返回值赋值的变量类型。
在你的第一个例子中,
Map<String, Object> model = ImmutableMap.of(
"post", post
);
如果post
被声明为Object
以外的任何内容,则会失败,因为Map<String, Anything>
不是Map<String, Object>
。
该方法声明为
public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {
编译器再次根据用法绑定type参数。您使用了post
,其中V
是预期的。 V
绑定到post
的声明类型。
您可以更明确并提供实际的类型参数
Map<String, Object> model2 = ImmutableMap.<String, Object>of(
"post", post
);
rules about type inference are extremely long有时很难理解。如果您愿意,可以尝试阅读它们。
答案 1 :(得分:1)
ImmutableMap.of()
的声明如下:
public static <K, V> ImmutableMap<K, V> of(K k1, V v1, ...)
因此默认情况下会返回键和值的推断类型,并且泛型不变(因此<String, PostType>
不是<String, Object>
的子类型。)
要强制它进入Map<String, Object>
,你需要做的就是使用(丑陋的)显式类型:
Map<String, Object> model = ImmutableMap.<String, Object>of("post", post)
答案 2 :(得分:0)
问题是编译器推断泛型类型并使用最具体的类型(声明的post
类型)。在点和Object
之间的<String, Object>
(或类型见证of
)添加显式广播。
答案 3 :(得分:0)
事实上,这整个情况是某些Java泛型功能的一个不幸后果,即Java使用 use-site variance (与声明 - 站点差异,例如在Scala中,以及Java集合接口也非常广泛。
Guava的ImmutableMap
,如下所示,是不可变的 - 在创建后不可能添加元素。因此,它自然是协变结构,也就是说,例如ImmutableMap<String, Number>
作为ImmutableMap<String, Object>
的子类型是绝对安全的。如果Java支持声明站点差异并使用它声明ImmutableMap
,那么,据说,您的代码将成功编译。
(但是会有一个问题.Java Map<K, V>
接口太“胖”:它需要变异方法,如put()
或remove()
。这意味着Map
界面本质上是不变的。我不知道声明站点差异在这种情况下会如何工作。可能它根本不起作用。)
无论如何,Java只有通配符形式的使用站点差异。您可以按如下方式重写代码:
Map<String, ?> model = ImmutableMap.of("post", post).
正如其他人已经说过的那样,ImmutableMap.of("post", post)
推断类型类似Map<String, Post>
,因为Post
是Object
的子类型,那么Map<String, Post>
是Map<String, ?>
的子类型,因此此代码将编译。此外,它还会阻止您在编译期间调用put()
和其他一些方法。