Понимание этого предупреждения: Сериализуемый класс не объявляет статический окончательный serialVersionUID
У меня есть статический код инициализатора:
someMethodThatTakesAHashMap(new HashMap<K, V>() {
{
put("a","value-a");
put("c","value-c");}
});
По какой-то причине я получаю предупреждение от Eclipse:
Сериализуемый класс не объявляет статический окончательный serialVersionUID.
Это жалоба на анонимный класс? Что я могу сделать по этому поводу, или я должен просто подавить его.
Ответы
Ответ 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;
}
}
Объявите многословие и успокоитесь, узнав, что ваши корпоративные сотрудники носят такие же кандалы, что и вы.