ラムダをシリアル化する方法は?


157

ラムダをエレガントにシリアル化するにはどうすればよいですか?

たとえば、次のコードはをスローしNotSerializableExceptionます。SerializableRunnable「ダミー」インターフェイスを作成せずに修正するにはどうすればよいですか?

public static void main(String[] args) throws Exception {
    File file = Files.createTempFile("lambda", "ser").toFile();
    try (ObjectOutput oo = new ObjectOutputStream(new FileOutputStream(file))) {
        Runnable r = () -> System.out.println("Can I be serialized?");
        oo.writeObject(r);
    }

    try (ObjectInput oi = new ObjectInputStream(new FileInputStream(file))) {
        Runnable  r = (Runnable) oi.readObject();
        r.run();
    }
}

18
これは可能ですが(選択した回答を参照)、実際にこれを行うことについては、おそらく誰もが2度考えるべきです。公式には「強く推奨されていません」ので、セキュリティに深刻な 影響を与える可能性があります。
David

回答:


260

Java 8では、複数の境界を追加することにより、オブジェクトを型の共通部分キャストする可能性が導入されています。したがって、シリアライゼーションの場合、次のように書くことができます。

Runnable r = (Runnable & Serializable)() -> System.out.println("Serializable!");

そしてラムダは自動的にシリアライズ可能になります。


4
非常に興味深い-この機能は非常に強力なようです。ラムダをキャストする以外にそのようなキャスト式の使用はありますか?たとえば、通常の匿名クラスでも同様のことができるようになりましたか?
Balder 2014

6
@Balderラムダの型推論のターゲット型を提供するために、交差型にキャストする機能が追加されました。AICにはマニフェストタイプがある(つまり、そのタイプは推論されない)ため、AICを交差タイプにキャストしても意味がありません。(それは可能ですが、役に立たないだけです。)AICに複数のインターフェースを実装させるには、それらすべてを拡張する新しいサブインターフェースを作成し、それをインスタンス化する必要があります。
スチュアートマークス

2
これにより、serialVersionUIDが定義されていないというコンパイラ警告が生成されますか?
Kirill Rakhman、2014

11
注:これは、構築中にキャストを適用した場合にのみ機能します。次はClassCastExceptionをスローします。Runnable r =()-> System.out.println( "Serializable!"); Runnable serializableR =(Runnable&Serializable)r;
bcody


24

同じ構成をメソッド参照に使用できます。たとえば、次のコード:

import java.io.Serializable;

public class Test {
    static Object bar(String s) {
        return "make serializable";
    }

    void m () {
        SAM s1 = (SAM & Serializable) Test::bar;
        SAM s2 = (SAM & Serializable) t -> "make serializable";
    }

    interface SAM {
        Object action(String s);
    }
}

ラムダ式と、シリアライズ可能なターゲットタイプを持つメソッド参照を定義します。


18

非常に醜いキャスト。私が使用している機能インターフェイスにシリアル化可能な拡張を定義したい

例えば:

interface SerializableFunction<T,R> extends Function<T,R>, Serializable {}
interface SerializableConsumer<T> extends Consumer<T>, Serializable {}

次に、ラムダを受け入れるメソッドを次のように定義できます。

private void someFunction(SerializableFunction<String, Object> function) {
   ...
}

そして、醜いキャストなしでラムダを渡すことができる関数を呼び出す:

someFunction(arg -> doXYZ(arg));

2
私がこの答えが好きなのは、あなたが書いていない外部の呼び出し元も自動的にシリアライズ可能になるからです。サブミットされたオブジェクトをシリアライズ可能にする場合は、インターフェースをシリアライズ可能にする必要があります。これは、インターフェースの要点の1つです。ただし、質問には「SerializableRunnable「ダミー」インターフェイスを作成せずに」と書かれていました
slevin

4

Beam / Dataflowコードの作成中に誰かがここに落ちた場合:

Beamには独自のSerializableFunctionインターフェイスがあるため、ダミーインターフェイスや詳細なキャストは必要ありません。


3

Kryoのような別のシリアライゼーションフレームワークに切り替えたい場合は、実装されたインターフェースが実装する必要がある複数の境界または要件を取り除くことができますSerializable。アプローチは

  1. InnerClassLambdaMetafactoryをシリアル化に必要なコードを常に生成するように変更します
  2. LambdaMetaFactory逆シリアル化中にを直接呼び出す

詳細とコードについては、このブログ投稿を参照してください

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.