Pourquoi mon java expression lambda ne peut pas travailler alors que son impératif de style fonctionne correctement?

0

La question

J'ai des années d'expérience de Java 8 et de son lambda. Mais j'ai rencontré un fou problème quand j'ai développé un hello-world-taille de l'Étincelle programme.

Ici, j'ai une classe Java, dans lequel les Données d'annotation est de Lombok:

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

Et puis j'ai construit un java liste contenant des objets de Persion classe:

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

donc bon jusqu'à présent. Et puis, j'ai essayé de générer une Étincelle Dataset à l'aide de la liste:

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

Notez que le bloc 1 est l'équivalent du bloc 2 de java et le bloc 2 est simplifiée du bloc 1 par IntelliJ IDEA. La seule différence est le bloc 2 est à l'aide d'expressions lambda.

Cependant, lorsque j'exécute le programme, le bloc 1 est bien qui finit bien, tout le bloc 2 exécuter dans l'exception: enter image description here

Ce que la... grande terre et grand univers? Pourquoi la JVM ou une Étincelle moteur fait des choses comme ça?!

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

La meilleure réponse

7

Comme expliqué dans Ce qui est l'équivalent d'expression lambda pour le Système.out::println, la méthode de référence System.out::println n'est pas identique à l'expression lambda x -> System.out.println(x).

La méthode de référence de capture de la valeur actuelle de System.outà invoquer println sur elle chaque fois que la fonction est appelée, plutôt que d'évaluer System.out chaque fois que le lambda expression du corps ne.

Comme l'a également dit, c'est rarement fait une différence, mais ici, il ne. Lorsque vous essayez de sérialiser la fonction, il va essayer de sérialiser toutes les valeurs, y compris la PrintStream exemple de lecture à partir de System.out lors de l'instanciation. L' PrintStream n'est pas sérialisable et il serait très difficile à mettre en œuvre un serializable PrintStream satisfaire les attentes.

Mais il est important de garder à l'esprit que lors de la sérialisation de l'expression lambda x -> System.out.println(x) ou un équivalent de la classe de l'objet et désérialiser dans un environnement différent, l' System.out il va lire, il y aura d'évaluer à un autre PrintStream que dans votre environnement d'origine. Ce n'est pas grave lorsque le calcul distribué framework prend soin de pipe tout affiché sur la sortie standard à l'expéditeur.

Mais il est important de garder à l'esprit que static les champs qui ne font pas partie des données sérialisées peuvent avoir des contenus différents dans des environnements différents, en général.

2021-11-24 08:36:53

Sonne comme il se produit uniquement avec System.out?Et je le remplace avec le Journal de cadre et bang! Il a réussi. ForeachFunction<String> functionBody = log::info;
Sheldon Wei

Dépend de la structure de journalisation. Il ne fonctionnera que si log est sérialisable.
Holger

Il semble ne se rapporte pas avec le cadre. J'utilise java.util.logging.Logger ce qui n'est pas sérialisable.
Sheldon Wei

Pas pour la norme de l'installation: ideone.com/F5lQZF “NotSerializableException: java.util.la journalisation.Logger”. Cependant, dans un environnement spécifique, un gestionnaire de journal peut retourner une sous-classe de Logger avec la sérialisation (ou RMI) de soutien, en outre, le cadre pourrait utiliser la sérialisation qui peut gérer des bûcherons dans une manière spéciale.
Holger
1

L'interface ForeachFunction s'étend Serializable. Dataset.foreach(f) peut-être la sérialisation de l'argument f. Dans le test suivant, testBlock1 réussit et testBlcok2 échoue (NotSerializableException). Mais je ne sais pas pourquoi.

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

J'ai testé votre cas et en effet, l'événement functionBody = t -> System.out.println(t) serait couronnée de succès. Donc, le problème de source je suppose que c'est la méthode de référence. Vous m'avez donné un énorme coup de main.
Sheldon Wei

Si la classe de test AAA ne pas mettre en œuvre Serializable dans mon code, testBlock1 échoueront également. L' functionBody dans testBlock1 est un anonyme, un intérieur de classe de la classe de test AAA et devrait être sérialisé avec une instance de la classe AAA qui l'enferme. Cependant, l' functionBody dans testBlock2 n'est pas un intérieur de classe AAA et ne semble pas mettre en œuvre Serializable en substance.
英語は苦手

Dans d'autres langues

Cette page est dans d'autres langues

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