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

the*_*oop 13 java lambda serializable java-8

我正在编写一个可序列化的类,它带有几个参数,包括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;        
    }
}
Run Code Online (Sandbox Code Playgroud)

func存储在成员变量中,因此需要可序列化.如果它们被分配的类型是可序列化的,则 Java lambdas 是可序列化的.Function如果使用lambda创建,那么确保我在构造函数中传递的最佳方法是可序列化的?

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

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

    问题:

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

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

    问题:

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

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

  3. 把它留给来电者

    这要求调用者知道(来自javadoc或者试错)参数需要可序列化,并在适当时进行转换:

    Cls c = new Cls(strList, (Function<String, ?> & Serializable)s -> ...);
    
    Run Code Online (Sandbox Code Playgroud)

    要么

    Cls c = new Cls(strList, (Function<String, ?> & Serializable)Foo::processStr);
    
    Run Code Online (Sandbox Code Playgroud)

    这是丑陋的IMO,并且使用lambda的初始天真实现保证会中断,而不是像coll(因为大多数集合以某种方式可序列化)一样工作.这也将类的实现细节推送到调用者.

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

编辑:也许需要一些背景知识.这是一个在暴风雨中运行的类,在一个bolt中,它被序列化以传输到一个删除集群来执行.该功能在群集上运行时对已处理的元组执行操作.因此,它可以序列化并且函数参数是可序列化的,这是类的目的的很大一部分.如果不是,则该类根本不可用.

Hol*_*ger 7

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

您可能会注意到JRE的大多数类,甚至ObjectOutputStream.writeObject不会Serializable在其签名中强制执行.有太多的API没有专门用于序列化,其中有关对象实现的编译时信息Serializable丢失并且与序列化一起使用它们需要大量的类型转换,如果后者强制执行它们的输入Serializable.

由于您的某个参数是a Collection,您可以从该API获取示例:

Collections.unmodifiableList:

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

您将发现更多这些操作,这些操作需要保留序列化功能,而不会Serializable在结果上保留编译时类型.

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


此外,每个具有更多用例而不仅仅是序列化的类应避免强制执行Serializable.这将阻碍不涉及序列化的用途.

关于Collection参数,您可以考虑删除可序列化约束.通常情况下,您可以保护您的课程免受以后对您收到的集合的更改.一个简单的解决方案是复制集合,当您执行此操作时,您可以使用支持序列化的类型.

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


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