2014-04-02 11 views
116

Jak mogę elegancko serializować lambda?Jak serializować lambda?

Na przykład poniższy kod powoduje wyświetlenie NotSerializableException. Jak mogę to naprawić, nie tworząc interfejsu "fikcyjnego" 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(); 
    } 
} 
+8

Chociaż jest to możliwe (patrz selekcja d odpowiedź), każdy powinien chyba dwa razy pomyśleć o zrobieniu tego. Jest oficjalnie ["mocno odradzany"] (https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#serialization) i może mieć [poważne] (http://stackoverflow.com/questions/25443655/możliwość-jawnego-usunięcia-serializacji-wsparcie-dla-a-lambda) [bezpieczeństwo] (https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-1-kryo) [Implikacje] (https://www.contrastsecurity.com/security-influencers/serialization-must-die-act-2-xstream). – David

Odpowiedz

191

Java 8 wprowadza możliwość cast an object to an intersection of types by adding multiple bounds. W przypadku serializacji można zatem napisać:

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

A lambda automagicznie staje się serializowalna.

+2

Bardzo interesujące - ta funkcja wydaje się być dość potężna. Czy jest jakikolwiek użytek z takiego rzucanego ekspresji poza castingiem lambdas? Na przykład. czy można teraz zrobić coś podobnego w zwykłej anonimowej klasie? – Balder

+0

Chodziło mi o to: czy istnieje sposób na utworzenie instancji anonimowej klasy implementującej dwa interfejsy podobne do utworzonej instancji lambda w twoim przykładzie. Ale po prostu przyjrzałem się temu i wydaje się, że nie ma nowej składni do robienia czegoś takiego. – Balder

+4

@Balder Dodano opcję rzutowania na typ przecięcia, aby podać typ celu dla wnioskowania typu lambda. Ponieważ AIC mają typ manifestu (tj., jego typ nie został wywnioskowany) rzucanie AIC do typu przecięcia nie jest użyteczne. (Jest to możliwe, po prostu nieprzydatne.) Aby mieć AIC zaimplementować wiele interfejsów, musisz stworzyć nowy podinterfejs, który rozszerza je wszystkie, a następnie utworzyć instancję. –

17

Ta sama konstrukcja może być użyta do odniesień do metod. Na przykład ten kod:

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

definiuje wyrażenie lambda i odwołanie do metody z docelowym typem szeregowania.

2

Jeśli chcesz przejść do innej architektury serializacji, takiej jak Kryo, możesz pozbyć się wielu ograniczeń lub wymagać, aby zaimplementowany interfejs implementował Serializable. Podejście jest

  1. Modyfikacja InnerClassLambdaMetafactory zawsze wygenerować kod wymagany do serializacji
  2. Bezpośrednio wywołać LambdaMetaFactory podczas deserializacji

po szczegóły i kod zobaczyć ten blog post

3

bardzo brzydki obsady . Wolę, aby zdefiniować Serializable rozszerzenie interfejsu funkcjonalnego Używam

Na przykład:

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

wówczas sposób akceptując lambda może być zdefiniowany jako takie:

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

i powołanie funkcja, którą możesz przekazać swojej lambdzie bez brzydkiej obsady:

someFunction(arg -> doXYZ(arg)); 
+1

Podoba mi się ta odpowiedź, ponieważ wtedy każdy zewnętrzny rozmówca, którego nie napiszesz, automatycznie będzie również możliwy do serializacji. Jeśli chcesz, aby przesyłane obiekty były przekształcalne do postaci szeregowej, twój interfejs powinien być możliwy do serializacji, co jest pewnego rodzaju punktem interfejsu. Jednak pytanie brzmiało "bez tworzenia" manekinowego "interfejsu" SerializableRunnable " – sjlevin

Powiązane problemy