Por qué mi java expresión lambda no puede trabajar mientras su imperativo estilo que funciona correctamente?

0

Pregunta

Tengo años de experiencia en Java 8 y su lambda. Pero conocí a un loco problema cuando he desarrollado un hola-mundo-el tamaño de la Chispa del programa.

Aquí tengo una clase de Java, en el que los Datos de la anotación es de Lombok:

@Data
public class Person implements Serializable {
  private String name;
  private Long age;
}

Y, a continuación, he construido un java de la lista que contiene los objetos de Persion clase:

        Person p1 = new Person("sb", 1L);
        Person p2 = new Person("sth", null);
        List<Person> list = new ArrayList<>(2);
        list.add(p1);
        list.add(p2);

tan bueno, tan lejos. Y entonces traté de generar una Chispa conjunto de datos utilizando la lista:

SparkSession session = SparkSession.builder().master("local[1]").appName("SparkSqlApp").getOrCreate();
Encoder<Person> personEncoder = Encoders.bean(Person.class);
Dataset<Person> dataset1 = session.createDataset(list, personEncoder);
dataset1.foreach(new ForeachFunction<Person>() { // 1
            @Override
            public void call(Person person) throws Exception {
                System.out.println(person);
            }
});
dataset1.foreach((ForeachFunction<Person>) System.out::println); //2

Observe que, el bloque 1 es equivalente a la del bloque 2 en java y el bloque 2 es simplificado del bloque 1 por IntelliJ IDEA. La única diferencia es el bloque 2 es el uso de la expresión lambda.

Sin embargo, cuando ejecuto el programa, bloque 1 termina bien, mientras que el bloque 2 de ejecución de excepción: enter image description here

Lo que el... grande de la tierra y del universo grande? ¿Por qué la JVM o Chispa del motor hace cosas como esta?!

apache-spark-sql java java-8 jvm
2021-11-24 03:11:05
2

Mejor respuesta

7

Como se explica en ¿Cuál es el equivalente de la expresión lambda para el Sistema.salida::println, el método de referencia System.out::println no es idéntica a la expresión lambda x -> System.out.println(x).

El método de referencia captura el valor actual de System.outpara invocar println en él cada vez que se invoca la función, en lugar de evaluar System.out cada vez como la expresión del cuerpo.

Como también se dijo, esto rara vez se hace una diferencia, pero aquí, no. Cuando se trate de serializar la función, se tratará de serializar todos los valores capturados, incluyendo la PrintStream ejemplo de lectura de System.out durante la creación de instancias. El PrintStream no es serializable y sería muy difícil de implementar un serializable PrintStream cumpliendo las expectativas.

Pero es importante tener en cuenta que cuando se serializa la expresión lambda x -> System.out.println(x) o equivalente en un objeto de la clase y deserializar en un entorno diferente, el System.out se va a leer no va a evaluar a una diferente PrintStream que en su entorno original. Esto no importa cuando la computación distribuida marco cuida a la tubería de todo lo imprime en la salida estándar al autor.

Pero es importante tener en cuenta que static los campos que no son parte de los datos serializados puede tener diferentes contenidos en los distintos ambientes en general.

2021-11-24 08:36:53

Suena como que sólo se produce con System.out?Y puedo reemplazar con Registro de marco y ¡bang! Fue un éxito. ForeachFunction<String> functionBody = log::info;
Sheldon Wei

Depende del marco de registros. Funcionará si log es serializable.
Holger

Parece que no se relacionan con el marco. Yo uso java.util.logging.Logger que no es serializable.
Sheldon Wei

No para la instalación estándar: ideone.com/F5lQZF "NotSerializableException: java.util.la tala de árboles.Logger". Sin embargo, en un entorno específico, un administrador del registro puede devolver una subclase de Logger con la serialización (o RMI) de apoyo, además, el marco se puede utilizar una extendida serialización que puede manejar los registradores de una manera especial.
Holger
1

La interfaz de ForeachFunction se extiende Serializable. Dataset.foreach(f) se puede serializar el argumento f. En la siguiente prueba, testBlock1 tiene éxito y testBlcok2 falla (NotSerializableException). Pero no sé por qué.

public class AAA implements Serializable {

    @FunctionalInterface
    public interface ForeachFunction<T> extends Serializable {
        void call(T t) throws Exception;
    }

    @Test
    public void testBlock1() throws FileNotFoundException, IOException {
        ForeachFunction<String> functionBody = new ForeachFunction<String>() {
            public void call(String t) throws Exception {
                System.out.println(t);
            }
        };
        try (FileOutputStream fos = new FileOutputStream("data/block1.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(functionBody);  // success
        }
    }

    @Test
    public void testBlock2() throws FileNotFoundException, IOException {
        ForeachFunction<String> functionBody = System.out::println;
        try (FileOutputStream fos = new FileOutputStream("data/block2.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(functionBody);  // fail (NotSerializableException)
        }
    }
}
2021-11-24 06:44:55

He probado el de los casos y, de hecho, evento functionBody = t -> System.out.println(t) sería un éxito. Así que el origen del problema supongo que es el método de referencia. Usted me dio una mano enorme.
Sheldon Wei

Si la clase de prueba AAA no aplicar Serializable en mi código, testBlock1 también se producirá un error. El functionBody en testBlock1 es una clase interna anónima de la clase de prueba AAA y debe ser serializado con una instancia de la clase AAA que lo encierre. Sin embargo, la functionBody en testBlock2 no es una clase interna de la clase AAA y no parece aplicar Serializable en la sustancia.
英語は苦手

En otros idiomas

Esta página está en otros idiomas

Русский
..................................................................................................................
Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Slovenský
..................................................................................................................