Как получить аргументы лямбда-выражения? - PullRequest
0 голосов
/ 07 августа 2020

Как я могу повторно проанализировать java .util.function.Consumer и получить аргументы и значения его лямбда-выражения (например, «student.person.surname»). Вскоре я хочу получить лямбда-выражение (Consumer) в виде текста во время выполнения.

import lombok.Data;

import java.util.function.Consumer;

public class ConsumerTest {

    @Data
    public static class Person{
        private Integer id;
        private String name;
        private String surName;
    }

    @Data
    public static class Student{
        private Integer id;
        private Person person;
    }

    public static void main(String args[])
    {
        Student student = new Student();
        student.setId(1);
        Person person = new Person();
        person.setName("Ali");
        person.setSurName("Veli");
        person.setId(2);
        student.setPerson(person);

        Consumer<Student> displayLambda = s -> s.getPerson().setSurName("Gulsoy");

        displayLambda.accept(student);

    //I want to reparse displaylambda instance and print arguments. 
    //As here I must be able to retrieve "student.person.surname" and "Gulsoy"
    }

}

Ответы [ 2 ]

0 голосов
/ 10 августа 2020

Большое спасибо за ответы. Я нашел способ решить свою проблему, используя de.danielbechler.diff.ObjectDifferBuilder, хотя я не читал точно лямбда-выражение. (студент) мы можем увидеть разницу между измененным объектом студента и предыдущим объектом студента. Таким образом, мы можем поймать измененный параметр и значение, как показано ниже.

import de.danielbechler.diff.ObjectDifferBuilder;
import de.danielbechler.diff.node.DiffNode;
import de.danielbechler.diff.node.Visit;
import lombok.Data;

import java.util.function.Consumer;

public class ConsumerTest {

    @Data
    public static class Person implements Cloneable{
        private Integer id;
        private String name;
        private String surName;

        public Person clone() throws CloneNotSupportedException {
            Person clonedObj = (Person) super.clone();
            clonedObj.name = new String(this.name);
            clonedObj.surName = new String(this.surName);
            clonedObj.id = new Integer(id);
            return clonedObj;
        }
    }

    @Data
    public static class Student implements Cloneable{
        private Integer id;
        private Person person;

        public Student clone() throws CloneNotSupportedException {
            Student clonedObj = (Student) super.clone();
            clonedObj.id = new Integer(id);
            clonedObj.person = this.person.clone();
            return clonedObj;
        }
    }

    public static void main(String args[])
    {
        Consumer<Student> displayLambda = s -> s.getPerson().setSurName("Gülsoy");

        Student student = new Student();
        student.setId(1);
        Person person = new Person();
        person.setName("Ali");
        person.setSurName("Veli");
        person.setId(2);
        student.setPerson(person);

        Student preStudent=null;

        // Before running consumer accept method, clone unchanged student object
        try {
            preStudent = student.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }

        // executing consumer lambda expression with accept method
        displayLambda.accept(student);

        // Checking difference with new Student instance and previous Student instance
        DiffNode diff = ObjectDifferBuilder.buildDefault().compare(preStudent, student);

        if (diff.hasChanges()) {
            Student finalPreStudent = preStudent;
            diff.visit((node, visit) -> {
                if (!node.hasChildren()) { // Only print if the property has no child
                    final Object oldValue = node.canonicalGet(finalPreStudent);
                    final Object newValue = node.canonicalGet(student);

                    // By doing this way we can catch parameter name and changing value
                    final String message = node.getPropertyName() + " changed from " +
                            oldValue + " to " + newValue;
                    System.out.println(message);
                }
            });
        } else {
            System.out.println("No differences");
        }
    }
}

0 голосов
/ 07 августа 2020

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: извините, но я не смог понять ваш вопрос, но я все же думаю, что мог бы поддержать добавление моих жестких навыков. Надеюсь, не так уж много "провалов" :)

Все дело в перспективе. Если мне нужно объяснить, что такое лямбда и как ее использовать, я буду использовать следующую абстракцию:

  1. Функциональный интерфейс:
  • Один абстрактный метод подпись (застряла с publi c, поскольку это член интерфейса -> обобщенные типы, тип возвращаемого значения, типы аргументов, предложение throws -> public <T> void processT(T t), public <T,R> R evalT(T t) et c.)
  • Может иметь ноль или больше не абстрактных методов (по умолчанию / stati c).
  • Абстрактный метод не имеет доступа к другим членам экземпляра в любой точке!
Lambdas:
  • Чистые реализации метода (или, как я их называю, реализации анонимных методов для известных функциональных интерфейсов). Чтобы компилятор мог распознать лямбда-оператор как действительный, он должен встретить особенность метода из любого известного функционального интерфейса во время компиляции (целевая лента не может быть аннотирована с помощью @FunctionalInterface вместо того, чтобы иметь один абстрактный метод и быть самим интерфейсом ( ref для абстрактных классов ).

Теперь давайте внимательнее рассмотрим ваш конкретный пример:

Consumer -> метод void, который принимает один аргумент (generi c, указанный тип) и выполняет некоторую обработку на основе ввода.

Давайте теперь рассмотрим ваш код и небольшие витрины, которые я добавил туда для вас.

import java.util.function.Consumer;

public class ConsumerTest {

    @Data
    public static class Person{
        private Integer id;
        private String name;
        private String surName;
    }

    @Data
    public static class Student{
        private Integer id;
        private Person person;
    }

    public static void main(String args[])
    {
        Student student = new Student();
        student.setId(1);
        Person person = new Person();
        person.setName("Ali");
        person.setSurName("Veli");
        person.setId(2);
        student.setPerson(person);

        /* shorthand definition for anonymous implementation in place, recognisable signature */
        Consumer<Student> displayLambda = s -> s.getPerson().setSurName("Gülsoy");

        /* full definition for anonymous implementation in place, allows annotations */
        Consumer<Student> anotherDisplayLambda = new Consumer<Student>() {

            @Override
            public void accept(Student student) {

                student.getPerson().setSurName("Gülsoy");
            }
        };

        // And finally:
        /* acquires reference to anonymous implementation with recognisable signature */
        Consumer<Student> yetAnotherDisplayLambda = ConsumerTest::thisIsAMethodButAlsoAConsumer;

        /* invokes the implementations, a.k.a. method call, method invocation */
        displayLambda.accept(student);
        anotherDisplayLambda.accept(student);
        yetAnotherDisplayLambda.accept(student);

        /* also valid statements, but this time it captures instance member, so make sure how it works under the hood */
        displayLambda = anotherDisplayLambda::accept; // same as `displayLambda = anotherDisplayLambda`
    }

    // if you can "retrieve that function" here than you should be able to answer your question as well...
    private static void thisIsAMethodButAlsoAConsumer(Student student) {

        student.getPerson().setSurName("Gülsoy");
    }
}

Теперь, давайте продолжим копать:

import java.util.function.Consumer;

public class ConsumerTest {

    @Data
    public static class Person{
        private Integer id;
        private String name;
        private String surName;
    }

    @Data
    public static class Student{
        private Integer id;
        private Person person;
    }
    private interface AnotherTypeOfInterface /* extends Consumer<Student> */
    {
        // if you can "retrieve that function" here than you should be able to answer your question as well...
        void consumeStudentObject(Student student);
    }
    
    public static void main(String args[])
    {
        Student student = new Student();
        student.setId(1);
        Person person = new Person();
        person.setName("Ali");
        person.setSurName("Veli");
        person.setId(2);
        student.setPerson(person);

        /* Target interface is not annotated as functional, still we got things done :)
         * If you comment out the extend clause in AnotherTypeOfInterface then @FunctionalInterface annotation will be required */
        AnotherTypeOfInterface anotherTypeOfConsumer = ConsumerTest::thisIsAMethodButAlsoAConsumer;

        /* throwsException in thread "main" java.lang.ClassCastException: ConsumerTest$$Lambda$3/2093631819 cannot be cast to
         * java.util.function.Consumer, unless you comment out the extend clause in interface definition */
//        Consumer<Student> interfacesAreStillTypes = anotherTypeOfConsumer;

        /* but this one doesn't throw as it parses all it needs -> anonymous method signature and definition... */
        Consumer<Student> yetAnotherTypeOfConsumer = anotherTypeOfConsumer::consumeStudentObject

        /* invokes the implementation */
        anotherTypeOfConsumer.consumeStudentObject(student);
//      interfacesAreStillTypes.accept(student);
        yetAnotherTypeOfConsumer.accept(student);

    }
}

В последнем примере AnotherTypeOfInterface будет иметь единственный метод с именем consumeStudentObject, который будет соответствовать Consumer::accept, однако экземпляры Consumer имеют свой собственный набор членов, например Consumer::andThen .

...