неизменяемая карта, добавление одного объекта является синтаксической ошибкой, 2 в порядке

1

Когда я добавляю объекты к неизменяемой карте, я это заметил.

если я добавлю один объект, например:

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

        );

Я получаю ошибку времени компиляции, говоря, что сообщение не является объектом.

Если я это сделаю:

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


        );

Он прекрасно компилируется. Есть ли способ для меня передать объект post объекту, чтобы он работал с одним объектом?

Или что еще более важно, почему это происходит?

Теги:
guava

4 ответа

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) {

Компилятор снова связывает параметр типа, основанный на использовании. Вы использовали post где ожидается V V привязан к любому объявленному типу post.

Вы можете быть более явным и предоставлять фактические аргументы типа

Map<String, Object> model2 = ImmutableMap.<String, Object>of(
    "post", post
);

Правила вывода типа чрезвычайно длинные и несколько раз трудно понять. Вы можете попробовать и прочитать их, если хотите.

1

Объявление ImmutableMap.of() выглядит так:

public static <K, V> ImmutableMap<K, V> of(K k1, V v1, ...)

Поэтому он будет возвращать по умолчанию предполагаемый тип ключа и значения, а generics являются инвариантными (поэтому <String, PostType> не является подтипом <String, Object>).

Чтобы заставить его использовать Map<String, Object>, вам нужно только использовать (уродливые) явные типы:

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

На самом деле, эта ситуация является неудачным следствием некоторых особенностей генерических возможностей Java, а именно того факта, что Java использует дисперсию использования сайта (в отличие от дисперсии объявления-сайта, например, в 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) inferred type будет выглядеть как Map<String, Post> и потому, что Post является подтипом Object, тогда Map<String, Post> является подтипом Map<String,?>, Поэтому этот код будет скомпилирован. Кроме того, это также мешает вам вызывать put() и некоторые другие методы во время компиляции.

0

Проблема заключается в том, что компилятор выводит общий тип и использует наиболее специфический (объявленный тип post). Добавить явное приведение к Object (или типа свидетель <String, Object> между точкой и of.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню