Почему мое лямбда-выражение java не может работать, пока его императивный стиль работает должным образом?

0

Вопрос

У меня есть многолетний опыт работы с Java 8 и ее лямбдой. Но я столкнулся с безумной проблемой, когда разработал программу Spark размером с hello world.

Здесь у меня есть класс Java, в котором аннотации данных взяты с Ломбока:

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

А затем я создал список java, содержащий объекты Persion класс:

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

пока все идет хорошо. А затем я попытался создать набор данных Spark, используя список:

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

Обратите внимание, что блок 1 эквивалентен блоку 2 в java, а блок 2 упрощен по сравнению с блоком 1 с помощью IntelliJ IDEA. Единственное отличие в том, что в блоке 2 используется лямбда-выражение.

Однако, когда я выполняю программу, блок 1 заканчивается хорошо, в то время как блок 2 выполняется в исключении: enter image description here

Что за... большая земля и большая вселенная? Почему JVM или двигатель Spark делают такие вещи?!

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

Лучший ответ

7

Как объясняется в разделе Что такое эквивалентное лямбда-выражение для System.out::println, ссылка на метод System.out::println не идентичен лямбда - выражению x -> System.out.println(x).

Ссылка на метод фиксирует текущее значение System.out, чтобы призвать println на нем каждый раз при вызове функции, а не при оценке System.out снова каждый раз, как это делает тело лямбда-выражения.

Как также было сказано, это редко имеет значение, но здесь это имеет значение. Когда вы попытаетесь сериализовать функцию, она попытается сериализовать все захваченные значения, включая PrintStream экземпляр, прочитанный из System.out во время создания экземпляра. То PrintStream не сериализуема, и было бы довольно сложно реализовать сериализуемую PrintStream оправдывает ожидания.

Но важно иметь в виду, что при сериализации лямбда-выражения x -> System.out.println(x) или эквивалентный объект класса и десериализовать его в другой среде, System.out там будет написано, что будет оцениваться по-другому PrintStream чем в вашем первоначальном окружении. Это не имеет значения, когда платформа распределенных вычислений заботится о том, чтобы передать все напечатанное в стандартный вывод обратно отправителю.

Но важно иметь в виду, что static поля, которые не являются частью сериализованных данных, могут иметь разное содержимое в разных средах в целом.

2021-11-24 08:36:53

Похоже, это происходит только с System.out?И я заменяю его рамкой журнала и бац! Это удалось. ForeachFunction<String> functionBody = log::info;
Sheldon Wei

Зависит от структуры ведения журнала. Это сработает, если log сериализуема.
Holger

Похоже, это никак не связано с рамками. Я использую java.util.logging.Logger который не поддается сериализации.
Sheldon Wei

Не для стандартной настройки: ideone.com/F5lQZF “Исключение NotSerializableException: java.util.ведение журнала.Лесоруб”. Однако в определенной среде менеджер журналов может возвращать подкласс Logger кроме того, при поддержке сериализации (или RMI) платформа может использовать расширенную сериализацию, которая может особым образом обрабатывать регистраторы.
Holger
1

Функция Foreach интерфейса расширяется Serializable. Dataset.foreach(f) возможно, это сериализация аргумента f. В следующем тесте, testBlock1 преуспевает и testBlcok2 сбой (исключение NotSerializableException). Но я не знаю почему.

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

Я проверил ваши дела и действительно, событие functionBody = t -> System.out.println(t) было бы успешно. Таким образом, источником проблемы, я полагаю, является ссылка на метод. Ты протянул мне огромную руку помощи.
Sheldon Wei

Если тестовый класс AAA не реализует Serializable в моем коде, testBlock1 тоже потерпит неудачу. То functionBody в testBlock1 является анонимным внутренним классом тестового класса AAA и должен быть сериализован с экземпляром класса AAA это заключает его в себе. Однако, в functionBody в testBlock2 не является внутренним классом класса AAA и, похоже, не реализует Serializable по существу.
英語は苦手

На других языках

Эта страница на других языках

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