Понимание этого предупреждения: Сериализуемый класс не объявляет статический финальный serialVersionUID
У меня есть статический код инициализатора:
someMethodThatTakesAHashMap(new HashMap<K, V>() {
{
put("a","value-a");
put("c","value-c");}
});
По какой-то причине я получаю предупреждение от Eclipse: сериализуемый класс не объявляет статический финальный serialVersionUID.
Это жалуется на анонимный класс? Что я могу сделать с этим, или я должен просто подавить это.
6 ответов
Синтаксис, который вы используете, называется двойной инициализацией - это фактически " блок инициализации экземпляра, который является частью анонимного внутреннего класса " (конечно, не хак). Таким образом, при использовании этой нотации вы фактически определяете новый класс (!).
"Проблема" в вашем случае заключается в том, что HashMap
инвентарь Serializable
, Этот интерфейс не имеет никаких методов и служит только для определения семантики сериализации. Другими словами, это интерфейс маркера, и вам конкретно не нужно ничего реализовывать. Но во время десериализации Java использует номер версии, называемый serialVersionUID
чтобы убедиться, что сериализованная версия совместима с целью. Если вы не предоставите это serialVersionUID
, будет рассчитано. И, как задокументировано в Javadoc Serializable
вычисленное значение является чрезвычайно чувствительным, и поэтому рекомендуется явно объявить его, чтобы избежать каких-либо проблем десериализации. И это то, на что "Затмение" "Затмение" (обратите внимание, что это всего лишь предупреждение).
Таким образом, чтобы избежать этого предупреждения, вы можете добавить serialVersionUID
к вашему анонимному внутреннему классу:
someMethodThatTakesAHashMap(new HashMap<String, String>() {
private static final long serialVersionUID = -1113582265865921787L;
{
put("a", "value-a");
put("c", "value-c");
}
});
Но вы теряете краткость синтаксиса (и он может вам даже не понадобиться).
Таким образом, другим вариантом будет игнорирование предупреждения путем добавления @SuppressWarnings("serial")
к методу, по которому вы звоните someMethodThatTakesAHashMap(Map)
, Это кажется более подходящим в вашем случае.
При этом, несмотря на то, что этот синтаксис лаконичен, у него есть некоторые недостатки. Во-первых, если вы удерживаете ссылку на объект, инициализированный с помощью двойной скобки, вы неявно удерживаете ссылку на внешний объект, который не будет пригоден для сборки мусора. Так что будьте осторожны. Во-вторых (хотя это звучит как микрооптимизация), инициализация двойной фигурной скобки приводит к небольшим накладным расходам. В-третьих, эта техника на самом деле использует анонимные внутренние классы, как мы видели, и, таким образом, потребляет немного пространства permgen (но я сомневаюсь, что это действительно проблема, если вы действительно не злоупотребляете ими). Наконец - и это, пожалуй, самый важный момент - я не уверен, что это делает код более читабельным (это не очень известный синтаксис).
Поэтому, хотя мне нравится использовать его в тестах (для краткости), я стараюсь избегать его использования в "обычном" коде.
Да, вы могли бы подавить предупреждение, но я бы переписал его так:
HashMap<String, String> map = new HashMap<String, String>();
map.put("a","value-a");
map.put("c","value-c");
someMethodThatTakesAHashMap(map);
Не нужно подавлять, и гораздо лучше читать, ИМО.
ImmutableMap
класс из библиотеки Google Collections полезен для этой ситуации. например
someMethodThatTakesAHashMap(ImmutableMap.<K, V>builder().put("a","value-a").put("c","value-c").build());
или же
someMethodThatTakesAHashMap(ImmutableMap.of("a","value-a","c","value-c"));
Чтобы ответить на другую половину вашего вопроса, "я должен подавить это?" -
Да. На мой взгляд, это ужасное предупреждение. serialVersionUID по умолчанию не должен использоваться, а не наоборот.
Если вы не добавите serialVersionUID, самое худшее, что происходит, - это то, что две версии объекта, которые на самом деле совместимы с сериализацией, считаются несовместимыми. serialVersionUID - это способ объявить, что совместимость сериализации не изменилась, переопределяя оценку Java по умолчанию.
При использовании serialVersionUID самое худшее, что происходит, - это то, что вы случайно не смогли обновить идентификатор, когда сериализованная форма класса изменяется несовместимым образом. В лучшем случае вы также получаете ошибку во время выполнения. В худшем случае случается нечто худшее. И представьте, как легко это не обновить.
Ваша цель состояла в том, чтобы инициализировать анонимный экземпляр HashMap. Предупреждение состоит в том, что ваш код делает больше, чем вы предполагали.
Мы ищем способ инициализации анонимного экземпляра HashMap. То, что мы имеем выше, создает анонимный подкласс HashMap, а затем создает анонимный экземпляр этого анонимного класса.
Поскольку код делает больше, чем предполагалось, я бы назвал это хаком.
Что мы действительно хотим, это что-то вроде этого:
foo(new HashMap<String, String>({"a", "value-a"}, {"c", "value-c"}));
Но, увы, это не относится к Java. Не существует способа сделать что-либо подобное безопасным для типов способом с использованием массива пар ключ / значение. Java просто не обладает выразительной силой.
Статические методы ImmutableMap.of в Google Collection близки, но это означает создание версии фабричного метода для различного числа пар ключ / значение. (См. Ответ finnw.)
Так что будь проще. Придерживайтесь решения Bart K, если ваш код не засорен этой инициализацией. Если это так, используйте ImmutableMap. Или сверните свой собственный подкласс HashMap с помощью методов фабрики стиля "of". Или создайте эти фабричные методы стиля "из" в служебном классе. Вот один для двух пар ключ / значение:
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;
}
}
Принимайте во внимание многословие и утешайтесь знаниями о том, что ваши корпоративные коллеги носят те же кандалы, что и вы.
Я в целом согласен с Бартом К., но в ознакомительных целях:
Предупреждение также можно устранить, добавив поле, которое может быть автоматически сгенерировано нажатием Ctrl+1.
Предупреждение также может быть подавлено добавлением аннотации @SuppressWarnings("serial") перед определением.
Анонимный класс реализует Serializeable, а Serializeable требует это статическое поле, чтобы можно было различать версии при сериализации и десериализации. Больше информации здесь:
http://www.javablogging.com/what-is-serialversionuid/