Dlaczego moje wyrażenie lambda java nie może pracować, dopóki jego императивный styl działa prawidłowo?

0

Pytanie

Mam wieloletnie doświadczenie w pracy z Java 8 i jej lâmbdoj. Ale mam do czynienia z szalonym problemem, gdy opracował program Spark wielkości hello world.

Tutaj mam klasę Java, w którym adnotacje danych pochodzą z Ломбока:

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

A następnie stworzyłem listę oprogramowania java, która zawiera obiekty Persion klasa:

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

jak na razie wszystko idzie dobrze. A następnie próbowałem utworzyć zestaw danych Spark, za pomocą listy:

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

Należy zwrócić uwagę, że blok 1 odpowiada bloku 2 w java, a blok 2 uproszczone w porównaniu z jednostką 1 za pomocą IntelliJ IDEA. Jedyna różnica polega na tym, że w bloku 2 stosuje się wyrażenie lambda.

Jednak, gdy mam do wykonania program, blok 1 kończy się dobrze, podczas gdy blok 2 jest w wyjątku: enter image description here

Co za... duża ziemia i wielki jest wszechświat? Dlaczego JVM lub silnik Spark robią takie rzeczy?!

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

Najlepsza odpowiedź

7

Jak wyjaśniono w sekcji Co to jest równoważne wyrażenie lambda System.out::printlnodniesienie do metoda System.out::println nie jest identyczny w wyrażeniu lambda x -> System.out.println(x).

Odniesienie do metoda rejestruje aktualną wartość System.outaby zachęcić println na nim za każdym razem, gdy wywoływana jest funkcja, a nie przy ocenie System.out ponownie za każdym razem, jak to sprawia, że ciało wyrażenia lambda.

Jak również wspomniano, jest to rzadko ma znaczenie, ale tutaj to ma znaczenie. Gdy spróbujesz serializować funkcję, ona spróbuje serializować wszystkie zarejestrowane wartości, w tym PrintStream egzemplarz, który z System.out podczas tworzenia instancji. To PrintStream nie сериализуема, i byłoby dość trudne do realizacji сериализуемую PrintStream z pewnością sprosta waszym oczekiwaniom.

Ale ważne jest, aby pamiętać, że podczas serializacji wyrażenia lambda x -> System.out.println(x) lub równoważny obiekt klasy i deserializacji go w innym środowisku, System.out tam będzie napisane, że będą oceniane inaczej PrintStream w swoim oryginalnym otoczeniu. To nie ma znaczenia, kiedy platforma obliczeń rozproszonych dba o to, aby przekazać wszystkie opublikowane w standardowe wyjście z powrotem do nadawcy.

Ale ważne jest, aby pamiętać, że static pola, które nie są częścią szeregowany danych, mogą mieć różną zawartość w różnych środowiskach w ogóle.

2021-11-24 08:36:53

Wygląda na to, że to się dzieje tylko z System.out?I zastępuję go ramką dziennika i bam! To udało. ForeachFunction<String> functionBody = log::info;
Sheldon Wei

Zależy od struktury prowadzenia dziennika. To zadziała, jeśli log сериализуема.
Holger

Wygląda na to, że to nie ma nic wspólnego z ramkami. Używam java.util.logging.Logger który nie nadaje się do serializacji.
Sheldon Wei

Nie dla standardowej konfiguracji: ideone.com/F5lQZF "Wyjątek NotSerializableException: java.util.prowadzenie dziennika.Drwal". Jednak w pewnym środowisku kierownik magazynów może zwracać podklasa Logger ponadto, przy wsparciu serializacji (lub RMI) platforma może korzystać z rozszerzoną serializację, które mogą w szczególny sposób przetwarzać rejestratory.
Holger
1

Funkcja Foreach interfejsu rozszerza Serializable. Dataset.foreach(f) może to szeregowania argument f. W poniższym teście testBlock1 udaje się i testBlcok2 zawiesza (wyjątek NotSerializableException). Ale nie wiem dlaczego.

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

Sprawdziłem swoje sprawy i naprawdę, wydarzenie functionBody = t -> System.out.println(t) byłoby to z powodzeniem. W ten sposób, że źródłem problemu, przypuszczam, jest odniesienie do metoda. Jesteś podał mi dużą dłoń.
Sheldon Wei

Jeśli test klasa AAA nie realizuje Serializable w moim kodzie, testBlock1 też nie udać. To functionBody w testBlock1 jest anonimowy wewnętrznych w klasie testowej klasy AAA i musi być serializowany z instancją klasy AAA to zawiera go w sobie. Jednak, w functionBody w testBlock2 nie jest wewnętrznym klasą klasy AAA i, jak się wydaje, nie realizuje Serializable w istocie.
英語は苦手

W innych językach

Ta strona jest w innych językach

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