使用guava Lists.transform时遇到的Java序列化问题

6
我需要对一个复杂对象进行序列化,但其组件之一是不可序列化的(第三方图形对象),因此我创建了一个自定义的可序列化版本的这个Graph类,并使用Guava List转换将不可序列化的对象转换为自定义对象。 但是,序列化writeObject仍然失败了。我很想知道原因是什么?我的假设是Lists.transform执行其操作时是惰性的(持有orginal对象的隐藏引用)。
另外,是否有解决此问题的方法?
3个回答

13

Lists.transform()确实像你猜测的那样具有延迟执行的特性。你可以选择以下其中一种方式:

Lists.newArrayList(Lists.transform(...))

或者,如果您想要一个不可变版本,

ImmutableList.copyOf(Lists.transform(...))

然后将生成的列表进行序列化。


你不需要创建新的列表,可以参考@eneveu的答案。 - Grzegorz Rożniecki
2
OP 表示 fromList 具有非序列化元素。 - Louis Wasserman
1
@Xaerness,你能修改一下你的评论吗?仅仅使用枚举技巧(@eneveu的回答)是无法获得可序列化列表的。这个回答是与问题最相关的。 - Anthony Chatellier

7

Lists.transform() 返回原始列表的转换视图。从 Lists.transform() 的 javadoc 中可以看到:

返回的列表总是实现 Serializable 接口,但只有当 fromList 和 function 都可序列化时,序列化才会成功。

在序列化转换视图时,实际上正在序列化原始列表和函数。在您的情况下,它失败了,因为原始列表不可序列化(因为它包含非可序列化的图形元素)。但也可能失败,因为该函数未实现可序列化接口。

顺便说一下,有一个小技巧可以创建可序列化的函数而不必冗长。不要这样做:

  private static final class MyFunction extends Function<String, String> implements Serializable {
    private static final MyFunction INSTANCE = new MyFunction();

    @Override
    public String apply(String input) {
      return "[" + input + "]";
    }

    private Object readResolve() {
      return INSTANCE;
    }
    private static final long serialVersionUID = 1;
  }

您可以使用枚举单例模式,这种方式更简洁,并且可以免费提供序列化功能(因为枚举是可序列化的)。它还确保了您的函数是单例的:

  // enum singleton pattern
  private enum MyFunction implements Function<String, String> {
    INSTANCE;

    @Override
    public String apply(String input) {
      return "[" + input + "]";
    }
  }

2
棒极了... 《Effective Java》。谢谢。 - uncaught_exceptions
1
是的,Joshua Bloch在《Effective Java》中解释说这是创建单例模式的最佳方式。枚举类型是可序列化的,不容易受到各种反序列化攻击,并且JVM保证其唯一性。您可以在Guava库中找到该模式的良好示例:http://code.google.com/p/guava-libraries/source/browse/guava/src/com/google/common/base/Functions.java#59 - Etienne Neveu
如果引用EJ并在Guava中应用良好的模式,即使答案不是很有用,也会获得额外的积分,因为fromList不会被序列化。 - Louis Wasserman

1
如果您尝试将由Lists#transform返回的列表序列化,那么List接口本身是不可序列化的。

1
为什么会有问题呢?在序列化期间,考虑的是实现方式。而Lists.transform()返回的实现确实实现了Serializable接口:“返回的列表总是实现Serializable接口,但只有当fromList和function都是可序列化的时,序列化才会成功。”(来自javadoc - Etienne Neveu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接