Java 如何使函数以通用方式序列化
我知道在我们需要它的地方,Java 如何使函数以通用方式序列化,java,generics,lambda,java-8,comparator,Java,Generics,Lambda,Java 8,Comparator,我知道在我们需要它的地方,Serializable 但是,我想将此转换转移到通用方法,以减少使用代码的混乱。 我无法创建这样的方法 我的具体问题是下面的映射不可序列化: final Map<MyObject, String> map = new TreeMap<>(Comparator.comparing(MyObject::getCode)); interface SerFunc<T, U> extends Function<T, U
Serializable
但是,我想将此转换转移到通用方法,以减少使用代码的混乱。
我无法创建这样的方法
我的具体问题是下面的映射不可序列化:
final Map<MyObject, String> map =
new TreeMap<>(Comparator.comparing(MyObject::getCode));
interface SerFunc<T, U> extends Function<T, U>, Serializable { }
我还尝试了以下备选方案,但没有成功:
// ClassCastException
public static <T extends Serializable, U extends Serializable> Function<T, U> makeSerializable(Function<T, U> function) {
return (Function<T, U> & Serializable) function;
}
// No ClassCastException, but NotSerializableException upon Serializing
public static <T, U> Function<T, U> makeSerializable2(Function<T, U> function) {
return (Function<T, U> & Serializable) t -> function.apply(t);
}
是的,只要不使用函数
作为参数或结果的类型,就有可能
相反,您可以创建自己的函数接口,它既是函数
又是可序列化的
:
final Map<MyObject, String> map =
new TreeMap<>(Comparator.comparing(MyObject::getCode));
interface SerFunc<T, U> extends Function<T, U>, Serializable { }
此方法的唯一目标是为作为参数传递的方法引用或lambda表达式提供可序列化的目标类型。这就是为什么我们只是返回论点,即什么也不做
现在,您可以在代码中使用该方法,一切都会正常工作:
Map<MyObject, String> map =
new TreeMap<>(Comparator.comparing(makeSerializable(MyObject::getCode)));
地图=
新的树映射(Comparator.comparing(makeSerializable(MyObject::getCode));
至于你的尝试失败的原因,我想你可以在(由@Eugene提供的链接)的答案中找到原因,Brian Goetz解释说这种行为是故意的: 这是正确的,而且是故意的。正如不能在实例化后获取不可序列化的对象并使其可序列化一样,创建lambda后,将设置其可序列化性 如果lambda的目标类型是可序列化的(并且其捕获的参数是可序列化的),则lambda是可序列化的
在您的尝试中,原始函数(由
makeSerializable
方法作为参数接收)不是Serializable
,因此我们创建的任何使用此不可序列化函数(实际上是捕获的参数)的lambda也将是不可序列化的。如果您愿意更改参数(我不确定所需的信息是否可以从输入函数中提取,但我可以尝试挖掘一些东西),您可以动态创建一个序列化的函数
,不确定这是否对您可行
public static <T, U> Function<T, U> makeSerializable(
Class<T> targetType, // MyObject
Class<U> returnType, // String
String methodName) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(returnType, targetType);
Function<T, U> fun = (Function<T, U>) LambdaMetafactory.altMetafactory(
lookup,
"apply",
MethodType.methodType(Function.class),
methodType,
lookup.findVirtual(targetType, methodName, MethodType.methodType(returnType)),
methodType,
1) // this signals for serialization
.getTarget().invokeExact();
return fun;
}
公共静态函数使序列化(
类targetType,//MyObject
类returnType,//字符串
字符串methodName)抛出可丢弃的{
MethodHandles.Lookup Lookup=MethodHandles.Lookup();
MethodType MethodType=MethodType.MethodType(返回类型,目标类型);
函数fun=(函数)LambdaMetafactory.altMetafactory(
查找,
“申请”,
MethodType.MethodType(Function.class),
方法类型,
lookup.findVirtual(targetType、methodName、MethodType.MethodType(returnType)),
方法类型,
1) //这表示要序列化
.getTarget().invokeExact();
返回乐趣;
}
这可能需要一个布尔值,并使其与静态方法引用一起工作
调用将是:
Map<MyObject, String> map = new TreeMap(Comparator.comparing(makeSerializable(
MyObject.class, String.class, "getCode")));
Map Map=newtreemap(Comparator.comparing(makeSerializable(
MyObject.class、String.class、“getCode”);
你能添加你的测试用例(或an)以便我们测试潜在的解决方案吗?@JacobG.他已经这样做了…@Eugene Where?我没有看到序列化问题中任何内容的代码。特别是,我想看看他是如何序列化他的对象的。@JacobG.我的错-它出现在现在删除的答案new ObjectOutputStream中(新建ByteArrayOutputStream()).writeObject(映射)
@Eugene噢,谢谢!我需要更快地到达10k,这样我才能看到!我喜欢这个答案,如果你不想像我在回答中所做的那样创建一个可序列化的功能接口,这是一个很好的选择。实际上,我并不真的喜欢这个,这似乎是为你捏造的me@Ward你问了这个问题,所以完全是这取决于您,但这是您在使用方法引用时在幕后发生的事情。无论如何,注释不用于扩展讨论;此对话已经结束。
Map<MyObject, String> map =
new TreeMap<>(Comparator.comparing(makeSerializable(MyObject::getCode)));
public static <T, U> Function<T, U> makeSerializable(
Class<T> targetType, // MyObject
Class<U> returnType, // String
String methodName) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType methodType = MethodType.methodType(returnType, targetType);
Function<T, U> fun = (Function<T, U>) LambdaMetafactory.altMetafactory(
lookup,
"apply",
MethodType.methodType(Function.class),
methodType,
lookup.findVirtual(targetType, methodName, MethodType.methodType(returnType)),
methodType,
1) // this signals for serialization
.getTarget().invokeExact();
return fun;
}
Map<MyObject, String> map = new TreeMap(Comparator.comparing(makeSerializable(
MyObject.class, String.class, "getCode")));