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")));