Aquí discutiremos la serialización en Java y los problemas relacionados con la función lambda sin la serialización, además de discutir algunas formas debido a las cuales requerimos la serialización junto con la implementación adecuada, como en los programas limpios de Java con un proceso completo de serialización y deserialización utilizando la interfaz de la función.
La serialización es un proceso para escribir el estado de un objeto en un flujo de bytes para que podamos transferirlo a través de la red. Podemos serializar una expresión lambda si su tipo de destino y sus argumentos capturados se han serializado. Sin embargo, al igual que las clases internas, se desaconseja encarecidamente la serialización de expresiones lambda. Como la función lambda no está serializada de forma predeterminada, simplemente tenemos que enviar nuestra lambda a java.io.Serializable. Pero el método de envío requiere un Runnable o Callable como parámetro, no un Serializable. Por lo tanto, tenemos que enviar la lambda a dos interfaces al mismo tiempo Runnable y Serializable.Java Serialization es una buena biblioteca de serialización generalizada y compatible con versiones anteriores. Dos de los problemas más comunes que las alternativas intentan resolver son el rendimiento y la serialización multiplataforma. En comparación, una serialización YAML binaria sencilla utiliza 348, con más opciones para optimizar la serialización. Esto plantea el problema de cómo serializar una lambda utilizando un formato de serialización alternativo, multiplataforma o más rápido.
Nota: Existe la necesidad de serializar una función lambda porque serializar lambdas puede ser útil en varios casos de uso, como la configuración persistente o como un patrón de visitante a recursos remotos.
Ilustración: Visitante remoto
Diga Queremos acceder a un recurso en un mapa remoto. Podemos usar get/put, pero digamos que solo queremos devolver un campo del valor de un Mapa: podemos pasar una lambda como visitante para extraer la información que queremos. Como puede ver, es fácil agregar varias funciones simples o llamar a un método para realizar la acción que necesita. El único problema es que las lambdas por defecto no son serializables.
Ejemplo
MapView userMap = Chassis.acquireMap("users", String.class, UserInfo.class); userMap.put("userid", new UserInfo("User's Name")); // Print out changes userInfo.registerSubscriber(System.out::println); // Obtain just the fullName without downloading the whole object String name= userMap.applyToKey("userid", u -> u.fullName); // Increment a counter atomically and trigger // an updated event printed with the subscriber. userMap.asyncUpdateKey("userid", ui -> { ui.usageCounter++; return ui; }); // Incrementing counter and return the userid int count = userMap.syncUpdateKey("userid", ui -> { ui.usageCounter++; return ui;}, ui -> ui.usageCounter);
Los compiladores y las bibliotecas utilizan la clase SerializedLambda para garantizar que las lambdas se deserialicen correctamente. Hacer el molde de intersección de Function<String, String> & Serializable cambia el tipo subyacente de la lambda, lo que permite que una biblioteca como Kryo comprenda correctamente cómo deserializar las lambdas que se le han dado.
Agregar esta conversión adicional de & Serializable es una posible solución para permitir que Kryo deserialice lambdas. Una ruta alternativa consiste en crear una nueva interfaz que amplíe el tipo de función subyacente que necesita, junto con Serializable.
Diseño:
public class IntersectionCasting { public static void main(String[] args) { SerializableLambda function = (message) -> "Kryo please serialize this message '" + message + "'"; } interface SerializableLambda extends Function<String, String>, Serializable {} }
Ahora, para hacer que Lambdas Serializable en nuestra API, se mantenga ante todo la seguridad de que, lamentablemente, las API estándar no se pueden cambiar o subclases para agregar esto, pero si tiene su propia API, puede usar una interfaz Serializable. El usuario de su API no tiene que decir explícitamente que la lambda es serializable. La implementación remota serializa la lambda, la ejecuta en el servidor y devuelve el resultado. De manera similar, existen métodos para aplicar una lambda al mapa como un todo.
Ejemplo:
Java
// Java Program to serialize and deserialize the lambda // function using a function interface // Importing input output classes import java.io.*; // Importing all function utility classes import java.util.function.*; // Interface interface MyInterface { // Method inside this interface void hello(String name); } // Class 1 // Helper class implementing above interface class MyImpl implements MyInterface { // Method of this class public void hello(String name) { System.out.println("Hello " + name); } } // Class 2 // Main class class GFG { // Method 1 // To Serialize private static void serialize(Serializable obj, String outputPath) throws IOException { File outputFile = new File(outputPath); if (!outputFile.exists()) { outputFile.createNewFile(); } try (ObjectOutputStream outputStream = new ObjectOutputStream( new FileOutputStream(outputFile))) { outputStream.writeObject(obj); } } // Method 2 // To Deserialize private static Object deserialize(String inputPath) throws IOException, ClassNotFoundException { File inputFile = new File(inputPath); try (ObjectInputStream inputStream = new ObjectInputStream( new FileInputStream(inputFile))) { return inputStream.readObject(); } } // Method 3 // To Serialize and deserialize lambda functions private static void serializeAndDeserializeFunction() throws Exception { Function<Integer, String> fn = (Function<Integer, String> & Serializable)(n) -> "Hello " + n; System.out.println("Run original function: " + fn.apply(10)); String path = "./serialized-fn"; serialize((Serializable)fn, path); System.out.println("Serialized function to " + path); Function<Integer, String> deserializedFn = (Function<Integer, String>)deserialize(path); System.out.println("Deserialized function from " + path); System.out.println("Run deserialized function: " + deserializedFn.apply(11)); } // Method 4 // To Serialize and deserialize lambda classes private static void serializeAndDeserializeClass() throws Exception { String path = "./serialized-class"; serialize(MyImpl.class, path); System.out.println("Serialized class to " + path); // Pretending we don't know the exact class of the // serialized bits, create an instance from the // class and use it through the interface. Class<?> myImplClass = (Class<?>)deserialize(path); System.out.println("Deserialized class from " + path); MyInterface instance = (MyInterface)myImplClass.newInstance(); instance.hello("Java"); } // Method 5 // Main driver method public static void main(String[] args) throws Exception { // Calling above method 3 and method 4 // in the main() body serializeAndDeserializeFunction(); serializeAndDeserializeClass(); } }
Producción:
Run original function: Hello 10 Serialized function to ./serialized-fn Deserialized function from ./serialized-fn Run deserialized function: Hello 11 Serialized class to ./serialized-class Deserialized class from ./serialized-class Hello Java