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

Какой лучший способ обеспечить аргумент Function можно сериализовать?

Я пишу сериализуемый класс, который принимает несколько аргументов, включая Function:

public class Cls implements Serializable {
    private final Collection<String> _coll;
    private final Function<String, ?> _func;

    public Cls(Collection<String> coll, Function<String, ?> func) {
        _coll = coll;
        _func = func;        
    }
}

func хранится в переменной-члене и поэтому нуждается в сериализации. Java lambdas являются сериализуемыми, если тип, которому они назначены, сериализуется. Какой лучший способ обеспечить, чтобы Function, который я получил в моем конструкторе, сериализуем, если он создан с помощью лямбда?

  • Создайте тип SerializableFunction и используйте это:

    public interface SerializableFunction<F, R> implements Function<F, R>, Serializable {}
    ....
    public Cls(Collection<String> coll, SerializableFunction<String, ?> func) {...}
    

    Вопросы:

    • В настоящее время существует несоответствие между аргументами coll и func, в том, что func объявляется сериализуемым в сигнатуре, но coll нет, но оба они должны быть сериализуемыми для работы.
    • Это не разрешает сериализацию других реализаций Function.
  • Используйте параметр типа в конструкторе:

    public <F extends Function<String, ?> & Serializable>
    Cls(Collection<String> coll, F func) {...}
    

    Вопросы:

    • Более гибкий, чем 1, но более запутанный.
    • До сих пор существует несоответствие между двумя аргументами - требуется аргумент func для реализации Serializable в иерархии типа времени компиляции, но coll просто требуется как-то сериализуться (хотя это требование может быть выполнено если необходимо).

    EDIT Этот код на самом деле не компилируется при попытке вызова с помощью ссылки лямбда или метода.

  • Оставьте его для вызывающего

    Это требует, чтобы вызывающий пользователь знал (из javadocs или проб и ошибок), что аргумент должен быть сериализуемым, и приведение в соответствие:

    Cls c = new Cls(strList, (Function<String, ?> & Serializable)s -> ...);
    

    или

    Cls c = new Cls(strList, (Function<String, ?> & Serializable)Foo::processStr);
    

    Это уродливая ИМО, и первоначальная наивная реализация использования лямбда гарантированно сломается, а не работает как с coll (поскольку большинство коллекций как-то сериализуется). Это также подталкивает деталь реализации класса к вызывающему.

В настоящий момент я склоняюсь к варианту 2, как тот, который налагает наименьшую нагрузку на вызывающего, но я не думаю, что здесь идеальное решение. Любые другие предложения относительно того, как это сделать должным образом?

EDIT. Возможно, необходим какой-то фон. Это класс, который запускается внутри storm в болте, который сериализуется для переноса на удаленный кластер для его выполнения. Функция выполняет операцию над обработанными кортежами при запуске в кластере. Таким образом, это очень большая часть цели класса, что она сериализуема и что аргумент функции сериализуем. Если это не так, то класс неприменим.

4b9b3361

Ответ 1

В большинстве случаев ответ таков: dont.

Вы можете заметить, что большинство классов JRE, даже ObjectOutputStream.writeObject не применяют Serializable в своей подписи. Просто слишком много API-интерфейсов, а не только для сериализации, когда информация времени компиляции об объекте, реализующем Serializable, теряется, и использование их вместе с Serialization потребует много литья типов, если последнее принудительно вводит их входы Serializable.

Поскольку один из ваших параметров - Collection, вы можете получить примеры из этого API:

Collections.unmodifiableList:

Возвращаемый список будет сериализован, если указанный список является сериализуемым.

Вы найдете больше этих операций, которые сохраняют возможность Serialization, не сохраняя при этом Serializable тип времени компиляции.

Это также относится ко всем типам не public, например. результаты Collections.emptyList(), Arrays.asList(…) и Comparator.reverseOrder(). Все они Serializable, не объявляя об этом.


Кроме того, каждый класс, имеющий больше случаев использования, чем просто сериализованный, должен воздерживаться от принудительного исполнения, чтобы быть всегда Serializable. Это будет препятствовать использованию, если не используется сериализация.

Что касается параметра Collection, вы можете вообще рассмотреть возможность удаления связанного с сериализацией ограничения. Как правило, вы защищаете свой класс от более поздних изменений в полученной вами коллекции. Простым решением является копирование коллекции, и когда вы это делаете, вы можете использовать тип, который поддерживает сериализацию.

Даже если вы хотите избежать копирования, сама Сериализация - это процесс копирования как таковой, поэтому вы можете просто создать собственные методы readObject и writeObject, хранящие содержимое Collection, устраняя необходимость иметь Serializable.


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