⛄️
シリアライザブルなラムダ式
ラムダ式はSerializable
にできます。
//キャストしたり
Supplier<String> s = (Supplier<String> & Serializable) () -> "x";
//メソッドであれしたり
<T extends Supplier<String> & Serializable> void consume(T supplier) { ... }
で、シリアライズできるぞー、と思ってこんなコード書いて、
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.function.Supplier;
public class Sample {
Supplier<String> get() {
return (Supplier<String> & Serializable) () -> toString();
}
public static void main(String[] args) throws IOException {
//↓シリアライザブルなサプライヤー
Supplier<String> s = new Sample().get();
//シリアライズしてみる
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ObjectOutputStream out = new ObjectOutputStream(baos)) {
out.writeObject(s);
}
}
}
実行すると、
Exception in thread "main" java.io.NotSerializableException: Sample
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeArray(ObjectOutputStream.java:1378)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1174)
at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at Sample.main(Sample.java:17)
例外です!
これはラムダ式でSample
クラスのtoString
メソッドを呼んでいるためSample
がキャプチャされますが、Sample
はSerializable
でないため例外が出ます。
Supplier<String> get() {
return (Supplier<String> & Serializable) () -> toString();
}
キャプチャというのはラムダ式内でラムダ式のスコープの外側の変数を参照した場合にラムダ式の実行環境に持ってくるっぽい感じのあれです。
キャプチャされているインスタンスはSerializedLambda
から取ってこれます。
SerializedLambda
はprivate finalなwriteReplace
メソッドで取ってこれます。
取ってこれるからと言ってカジュアルに呼んで良いメソッドではないです。
writeReplace
についてはSerializable
のJavadocに書いてあります。
こんな感じでSerializedLambda
とキャプチャしたインスタンスを取ってこれました。
Supplier<String> s = new Sample().get();
Method m = s.getClass().getDeclaredMethod("writeReplace");
m.setAccessible(true);
SerializedLambda sl = (SerializedLambda) m.invoke(s);
for (int i = 0; i < sl.getCapturedArgCount(); i++) {
System.out.println(sl.getCapturedArg(i));
}
この辺りをもっといじくりまわすと面白い事ができそうな気がしなくもないですね!
参考リソース
Discussion