确保函数参数可序列化的最佳方法是什么?
What's the best way of ensuring a Function argument is serializable?
我正在编写一个可序列化的 class,它接受多个参数,包括 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 lambda are serializable if the type they're being assigned to is serializable。如果使用 lambda 创建,确保我在构造函数中传递的 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 当尝试使用 lambda 或方法引用调用时,此代码实际上并未编译。
留给来电者吧
这要求调用者知道(从 javadoc 或反复试验)参数需要是可序列化的,并进行适当的转换:
Cls c = new Cls(strList, (Function<String, ?> & Serializable)s -> ...);
或
Cls c = new Cls(strList, (Function<String, ?> & Serializable)Foo::processStr);
这在我看来是丑陋的,使用 lambda 的最初天真的实现 保证 会中断,而不是像 coll
那样工作(因为大多数集合以某种方式可序列化)。这也将 class 的 实现 细节推送给调用者。
目前我倾向于选项 2,因为它给调用者带来的负担最小,但我认为这里没有理想的解决方案。关于如何正确执行此操作的任何其他建议?
编辑:也许需要一些背景知识。这是一个运行在storm里面的class,在一个bolt中,它被序列化以传输到一个remove cluster来执行。当 运行 在集群上时,该函数正在对已处理的元组执行操作。因此,class 的目的的很大一部分是它是可序列化的,并且函数参数是可序列化的。如果不是,则 class 根本无法使用。
大多数情况下答案是:不要。
您可能会注意到大多数 class 的 JRE,甚至 ObjectOutputStream.writeObject
都不会在其签名中强制执行 Serializable
。有太多 API 不是专门针对序列化的,其中有关实现 Serializable
的对象的编译时信息丢失,如果后者强制执行输入,将它们与序列化一起使用将需要大量类型转换成为 Serializable
.
由于您的参数之一是 Collection
,您可能会从中获得示例 API:
The returned list will be serializable if the specified list is serializable.
您会发现更多这样的操作,它们关心保留序列化功能而不保留结果的 Serializable
编译时类型。
这也适用于所有非public
类型,例如Collections.emptyList()
、Arrays.asList(…)
和 Comparator.reverseOrder()
的结果。都是Serializable
没有声明的
此外,每个 class 的用例比仅仅被序列化的用例都多,应该避免强制总是 Serializable
。这将阻碍不涉及序列化的用途。
关于Collection
参数,您可以考虑完全删除可序列化约束。通常,您会保护您的 class 免受以后对您收到的集合的更改。一个简单的解决方案是复制集合,当您这样做时,您可以使用支持序列化的类型。
即使您想避免复制,序列化本身就是一个复制过程,因此您可以简单地创建自定义 readObject
和 writeObject
方法来存储 内容 的 Collection
,无需 Serializable
集合。
总而言之,通常的策略是 如果 您的 class 的用户打算序列化它的实例,则用户有责任将所有组件放入的是他们自己 Serializable
.
我正在编写一个可序列化的 class,它接受多个参数,包括 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 lambda are serializable if the type they're being assigned to is serializable。如果使用 lambda 创建,确保我在构造函数中传递的 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 当尝试使用 lambda 或方法引用调用时,此代码实际上并未编译。
留给来电者吧
这要求调用者知道(从 javadoc 或反复试验)参数需要是可序列化的,并进行适当的转换:
Cls c = new Cls(strList, (Function<String, ?> & Serializable)s -> ...);
或
Cls c = new Cls(strList, (Function<String, ?> & Serializable)Foo::processStr);
这在我看来是丑陋的,使用 lambda 的最初天真的实现 保证 会中断,而不是像
coll
那样工作(因为大多数集合以某种方式可序列化)。这也将 class 的 实现 细节推送给调用者。
目前我倾向于选项 2,因为它给调用者带来的负担最小,但我认为这里没有理想的解决方案。关于如何正确执行此操作的任何其他建议?
编辑:也许需要一些背景知识。这是一个运行在storm里面的class,在一个bolt中,它被序列化以传输到一个remove cluster来执行。当 运行 在集群上时,该函数正在对已处理的元组执行操作。因此,class 的目的的很大一部分是它是可序列化的,并且函数参数是可序列化的。如果不是,则 class 根本无法使用。
大多数情况下答案是:不要。
您可能会注意到大多数 class 的 JRE,甚至 ObjectOutputStream.writeObject
都不会在其签名中强制执行 Serializable
。有太多 API 不是专门针对序列化的,其中有关实现 Serializable
的对象的编译时信息丢失,如果后者强制执行输入,将它们与序列化一起使用将需要大量类型转换成为 Serializable
.
由于您的参数之一是 Collection
,您可能会从中获得示例 API:
The returned list will be serializable if the specified list is serializable.
您会发现更多这样的操作,它们关心保留序列化功能而不保留结果的 Serializable
编译时类型。
这也适用于所有非public
类型,例如Collections.emptyList()
、Arrays.asList(…)
和 Comparator.reverseOrder()
的结果。都是Serializable
没有声明的
此外,每个 class 的用例比仅仅被序列化的用例都多,应该避免强制总是 Serializable
。这将阻碍不涉及序列化的用途。
关于Collection
参数,您可以考虑完全删除可序列化约束。通常,您会保护您的 class 免受以后对您收到的集合的更改。一个简单的解决方案是复制集合,当您这样做时,您可以使用支持序列化的类型。
即使您想避免复制,序列化本身就是一个复制过程,因此您可以简单地创建自定义 readObject
和 writeObject
方法来存储 内容 的 Collection
,无需 Serializable
集合。
总而言之,通常的策略是 如果 您的 class 的用户打算序列化它的实例,则用户有责任将所有组件放入的是他们自己 Serializable
.