不可变映射,添加单个对象是语法错误,2很好

时间:2014-03-17 00:56:56

标签: java guava

当我将对象添加到不可变地图时,我注意到了这一点。

如果我添加一个像这样的对象:

 Map<String, Object> model = ImmutableMap.of(
                "post", post

        );

我收到编译时错误,说帖子不是对象。

如果我这样做:

 Map<String, Object> model = ImmutableMap.of(
                "post", post,
                "asdf", new Object()


        );

编译好。有没有办法让post对象转换为一个对象,以便它可以使用单个对象?

或者更重要的是,为什么会发生这种情况?

4 个答案:

答案 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()
);

其中postnew 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>,因为PostObject的子类型,那么Map<String, Post>Map<String, ?>的子类型,因此此代码将编译。此外,它还会阻止您在编译期间调用put()和其他一些方法。