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

StackOverflow https://stackoverflow.com/questions/1514881

Вопрос

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

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

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

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

Это было полезно?

Решение

Синтаксис, который вы используете, называется Двухклассная инициализация - что на самом делеБлок инициализации экземпляра это часть анонимный внутренний класс«(Конечно, не взломать). Поэтому при использовании этого нотации вы на самом деле определяете новый класс (!).

«Проблема» в вашем случае заключается в том, что 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). Анкет Это кажется более подходящим в вашем случае.

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

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

Другие советы

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

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

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

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

А 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 Simple не обладает выразительной силой.

Статические методы Google Collection. (См. Ответ FINNW.)

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

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;
    }
}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top