Подтвердить что ты не робот

Понимание этого предупреждения: Сериализуемый класс не объявляет статический окончательный serialVersionUID

У меня есть статический код инициализатора:

someMethodThatTakesAHashMap(new HashMap<K, V>() {
{
  put("a","value-a"); 
  put("c","value-c");}
});

По какой-то причине я получаю предупреждение от Eclipse: Сериализуемый класс не объявляет статический окончательный serialVersionUID.

Это жалоба на анонимный класс? Что я могу сделать по этому поводу, или я должен просто подавить его.

4b9b3361

Ответ 1

Используемый вами синтаксис называется инициализация двойной привязки - на самом деле это " блок инициализации экземпляра, который является частью анонимного внутреннего класса" (конечно же, не взломать). Таким образом, при использовании этих обозначений вы фактически определяете новый класс (!).

"Проблема" в вашем случае заключается в том, что HashMap реализует Serializable. Этот интерфейс не имеет никаких методов и служит только для определения семантики сериализации. Другими словами, это интерфейс маркера, и вам конкретно не нужно ничего реализовывать. Но, во время десериализации Java использует номер версии с именем serialVersionUID, чтобы убедиться, что сериализованная версия совместима с целевым. Если вы не укажете этот serialVersionUID, он будет рассчитан. И, как описано в javadoc Serializable, вычисленное значение чрезвычайно чувствительно, и поэтому рекомендуется явно заявить об этом, чтобы избежать проблемы десериализации. И это то, о чем жалуется Eclipse (обратите внимание, что это всего лишь предупреждение).

Итак, чтобы избежать этого предупреждения, вы можете добавить serialVersionUID в свой анонимный внутренний класс:

someMethodThatTakesAHashMap(new HashMap<String, String>() {
    private static final long serialVersionUID = -1113582265865921787L;

    {
        put("a", "value-a");
        put("c", "value-c");
    }
});

Но вы теряете краткость синтаксиса (и вам это может даже не понадобиться).

Другим вариантом является, таким образом, игнорировать предупреждение, добавив @SuppressWarnings("serial") к методу, в котором вы вызываете someMethodThatTakesAHashMap(Map). Это кажется более подходящим в вашем случае.

Что все сказано, хотя этот синтаксис является кратким, он имеет некоторые недостатки. Во-первых, если вы держите ссылку на объект, инициализированный с помощью инициализации с двойной привязкой, вы неявно держите ссылку на внешний объект, который не будет иметь права на сбор мусора. Так что будь осторожен. Во-вторых (это похоже на микро-оптимизацию, хотя), инициализация с двойной привязкой имеет очень немного накладных расходов. В-третьих, этот метод фактически использует анонимные внутренние классы по мере того, как мы видели, и, таким образом, ест немного пространства с пергеном (но я сомневаюсь, что это действительно проблема, если вы не действительно злоупотребляете ими). Наконец - и это, возможно, самый важный момент - я не уверен, что он делает код более читаемым (это не очень известный синтаксис).

Итак, хотя мне нравится использовать его в тестах (для краткости), я стараюсь избегать использования его в "правильном" коде.

Ответ 2

Да, вы можете подавить предупреждение, но я бы переписал его так:

HashMap<String, String> map  = new HashMap<String, String>();
map.put("a","value-a"); 
map.put("c","value-c");
someMethodThatTakesAHashMap(map);

Не нужно подавлять, и намного лучше читать, ИМО.

Ответ 3

Я вообще согласен с Бартом К., но для информационных целей:
Предупреждение также можно устранить, добавив поле, которое можно автоматически сгенерировать, нажав Ctrl + 1.
Предупреждение также может быть подавлено добавлением аннотации @SuppressWarnings ( "serial" ) до определения.
Анонимный класс реализует Serializeable, а Serializeable требует этого статического поля, чтобы можно было различать версии при сериализации и де-сериализации. Дополнительная информация здесь:
http://www.javablogging.com/what-is-serialversionuid/

Ответ 4

Класс ImmutableMap из библиотеки коллекций Google полезен для этой ситуации. например.

someMethodThatTakesAHashMap(ImmutableMap.<K, V>builder().put("a","value-a").put("c","value-c").build());

или

someMethodThatTakesAHashMap(ImmutableMap.of("a","value-a","c","value-c"));

Ответ 5

Чтобы решить другую половину вашего вопроса, "следует ли мне его подавить?" -

Да. На мой взгляд, это ужасное предупреждение. serialVersionUID по умолчанию не должен использоваться, а не наоборот.

Если вы не добавите serialVersionUID, самое худшее, что происходит, это то, что две версии объекта, которые фактически совместимы с сериализацией, считаются несовместимыми. serialVersionUID - это способ объявить, что совместимость с сериализацией не изменилась, переопределяя оценку по умолчанию Java.

Используя serialVersionUID, самое худшее, что происходит, это то, что вы случайно не обновляете идентификатор, когда сериализованная форма класса изменяется несовместимым образом. В лучшем случае вы также получите ошибку времени выполнения. В худшем случае происходит что-то хуже. И представьте, как легко не обновлять его.

Ответ 6

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

То, что мы ищем, - это способ инициализации анонимного экземпляра HashMap. То, что мы имеем выше, создает анонимный подкласс HashMap, затем создает анонимный экземпляр этого анонимного класса.

Поскольку код делает больше, чем предполагалось, я бы назвал это взломом.

Мы действительно хотим что-то вроде этого:

foo(new HashMap<String, String>({"a", "value-a"}, {"c", "value-c"}));

Но, увы, это неправда Java. Нет способа сделать что-либо это безопасным типом, используя массив пар ключ/значение. Java simple не обладает выразительной способностью.

Статические методы Google Collection ImmutableMap.of близки, но это означает создание версии метода factory для различных номеров пар ключ/значение. (См. Окончательный ответ.)

Так держать вещи просто. Пойдите с решением Bart K, если ваш код не завален этой инициализацией. Если так, используйте ImmutableMap. Или сверните свой собственный подкласс HashMap с помощью методов "стиля" factory. Или создайте эти методы "стиля" factory в классе утилиты. Здесь одна из двух пар ключ/значение:

public final MapUtil {
    public static <K,V> Map<K,V> makeMap(K k1, V v1, K k2, V v2) {
        Map<K,V> m = new HashMap<K,V>();
        m.put(k1, v1);
        m.put(k2, v2);
        return m;
    }
}

Объявите многословие и успокоитесь, узнав, что ваши корпоративные сотрудники носят такие же кандалы, что и вы.