确保函数参数可序列化的最佳方法是什么?

2022-09-04 22:42:21

我正在编写一个可序列化的类,它接受几个参数,包括: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 被分配到的类型是可序列化的,那么它们是可序列化的。如果 I 在构造函数中传递是可序列化的,如果它是使用 lambda 创建的,那么确保 I 在构造函数中传递的最佳方法是什么?Function

  1. 创建一个类型并使用:SerializableFunction

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

    问题:

    • 现在,和 参数之间存在不匹配,因为在签名中声明为可序列化,但不是,但两者都需要可序列化才能正常工作。collfuncfunccoll
    • 它不允许其他可序列化的实现。Function
  2. 在构造函数上使用类型参数:

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

    问题:

    • 比1更灵活,但更令人困惑。
    • 这两个参数之间仍然存在不匹配 - 该参数需要在编译时类型层次结构中实现,但只需要以某种方式进行序列化(尽管如果需要,可以丢弃此要求)。funcSerializablecoll

    编辑尝试使用 lambda 或方法引用进行调用时,此代码实际上不会编译。

  3. 留给呼叫者

    这要求调用方知道(来自javadocs或试错法)参数需要可序列化,并根据需要强制转换:

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

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

    这是丑陋的IMO,并且使用lambda的初始天真实现肯定会中断,而不是像使用一样工作(因为大多数集合都可以以某种方式序列化)。这还会将类的实现详细信息推送到调用方。coll

目前,我倾向于选项2,因为对呼叫者施加的负担最小,但我认为这里没有理想的解决方案。关于如何正确执行此操作的任何其他建议?

编辑:也许需要一些背景知识。这是一个在 storm 内部以 bolt 形式运行的类,该类被序列化以传输到 remove 群集以执行。在集群上运行时,该函数正在对已处理的元组执行操作。因此,该类的用途的很大一部分是它是可序列化的,并且函数参数是可序列化的。如果不是,则该类根本不可用。


答案 1

在大多数情况下,答案是:不要

您可能会注意到,JRE 的大多数类,甚至 ObjectOutputStream.writeObject 都不会在其签名中强制执行。有太多的 API 不是专门针对序列化的,其中有关对象实现的编译时信息丢失,如果序列化强制其输入为 .SerializableSerializableSerializable

由于您的一个参数是 ,您可以从该 API 获得示例:Collection

Collections.unmodifiableList

如果指定的列表是可序列化的,则返回的列表将是可序列化的。

您会发现更多这样的操作,它们需要保留序列化功能,而不会在结果上保留编译时类型。Serializable

这也适用于所有非类型,例如 、 和 的结果。他们都没有宣布。publicCollections.emptyList()Arrays.asList(…)Comparator.reverseOrder()Serializable


此外,每个类都有比序列化更多的用例,应该避免强制始终。这将阻碍不涉及序列化的使用。Serializable

关于参数,您可以考虑完全删除可序列化的约束。通常,您可以保护您的类,防止以后对收到的集合进行更改。一个简单的解决方案是复制集合,当您这样做时,可以使用支持序列化的类型。Collection

即使您想避免复制,序列化本身也是一个复制过程,因此您可以简单地创建自定义和方法来存储的内容,而无需拥有集合。readObjectwriteObjectCollectionSerializable


总而言之,通常的策略是,如果类的用户打算序列化它的实例,则用户有责任将所有组件放入其中。Serializable


答案 2

推荐