英文:
How to retrieve arguments of a lambda expression?
问题
如何重新解析 java.util.function.Consumer 实例并检索其 lambda 表达式的参数和值(例如 "student.person.surname")。简而言之,我想在运行时将 lambda 表达式(Consumer)作为文本检索出来。
// 我想要重新解析 displayLambda 实例并打印参数。
// 就像这里我必须能够检索 "student.person.surname" 和 "Gulsoy" 一样。
英文:
How can I reparse java.util.function.Consumer instance and retrieve arguments and values of its lambda expression (forexample "student.person.surname"). Shortly I want to retrieve lambda expression (Consumer) as a text at runtime.
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"
}
}
答案1
得分: 0
感谢您提供的答案。尽管我还没有完全理解 lambda 表达式,但我已经通过使用 de.danielbechler.diff.ObjectDifferBuilder 找到了解决我的问题的方法。
在运行消费者 accept 方法之前,会克隆学生对象,然后在执行 displayLambda.accept(student) 后,我们可以获取更改后的学生对象与先前学生对象之间的差异。因此,我们可以捕获到下面所示的更改参数和值。
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\u00FClsoy");
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;
// 在运行消费者 accept 方法之前,克隆未更改的学生对象
try {
preStudent = student.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
// 执行带有 accept 方法的消费者 lambda 表达式
displayLambda.accept(student);
// 使用新的 Student 实例和以前的 Student 实例检查差异
DiffNode diff = ObjectDifferBuilder.buildDefault().compare(preStudent, student);
if (diff.hasChanges()) {
Student finalPreStudent = preStudent;
diff.visit((node, visit) -> {
if (!node.hasChildren()) { // 仅在属性没有子项时打印
final Object oldValue = node.canonicalGet(finalPreStudent);
final Object newValue = node.canonicalGet(student);
// 通过这种方式,我们可以捕获参数名称和更改后的值
final String message = node.getPropertyName() + " changed from " +
oldValue + " to " + newValue;
System.out.println(message);
}
});
} else {
System.out.println("No differences");
}
}
}
希望这对您有所帮助。
英文:
Thanks a lot for the answers. I have found a way to my problem by using de.danielbechler.diff.ObjectDifferBuilder even though I haven't read lambda expression exactly.
Before running consumer accept method, student object is cloned and after executing displayLambda.accept(student) we can get the difference between changed student object and previous student object. And so we can catch changed parameter and value as seen below.
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");
}
}
}
答案2
得分: -1
免责声明:抱歉,我无法理解您的问题,但我认为我可以加入我的想法以提供帮助。希望不会有太多的“downs”
这一切都关乎观点。如果我必须解释什么是 lambda 及如何使用它,那么我将使用以下抽象概念:
- 函数接口:
- 单一抽象方法签名(由于它是一个接口成员,需要使用
public
修饰符 -> 泛型、返回类型、参数类型、异常声明 ->public <T> void processT(T t)
、public <T,R> R evalT(T t)
等)。 - 可以有零个或多个非抽象方法(默认/静态)。
- 抽象方法在任何时候都无法访问其他实例成员!
- Lambda 表达式:
- 纯方法实现(或者我称之为已知函数接口的匿名方法实现)。为了让编译器在编译时将 lambda 表达式识别为有效的表达式,它应该符合已知函数接口中的任何方法签名(目标类型可以没有用 @FunctionalInterface 注释,而是具有单一抽象方法并且本身是接口(参考抽象类的情况)。
现在,让我们更仔细地看看您的具体示例:
Consumer<Type> -> 接受一个参数(泛型、指定类型)并根据输入执行一些处理的 void 方法。
现在让我们考虑您的代码,以及我为您添加的一些小示例。
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("Gülsoy");
/* 在现场中对匿名实现的完整定义,允许注释 */
Consumer<Student> anotherDisplayLambda = new Consumer<Student>() {
@Override
public void accept(Student student) {
student.getPerson().setSurName("Gülsoy");
}
};
// 最后:
/* 获取可识别签名的匿名实现的引用 */
Consumer<Student> yetAnotherDisplayLambda = ConsumerTest::thisIsAMethodButAlsoAConsumer;
/* 调用实现,即方法调用,方法调用 */
displayLambda.accept(student);
anotherDisplayLambda.accept(student);
yetAnotherDisplayLambda.accept(student);
/* 也是有效的语句,但这次它捕获了实例成员,所以确保了解它在幕后的工作原理 */
displayLambda = anotherDisplayLambda::accept; // 与 `displayLambda = anotherDisplayLambda` 相同
}
// 如果您可以在此处“检索该函数”,那么您应该能够回答您的问题...
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 /* 扩展 Consumer<Student> */
{
// 如果您可以在此处“检索该函数”,那么您应该能够回答您的问题...
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);
/* 目标接口未标记为功能性接口,但我们仍然可以完成任务 :)
* 如果您注释掉 AnotherTypeOfInterface 中的扩展子句,那么将需要 @FunctionalInterface 注释 */
AnotherTypeOfInterface anotherTypeOfConsumer = ConsumerTest::thisIsAMethodButAlsoAConsumer;
/* 抛出异常:java.lang.ClassCastException: ConsumerTest$$Lambda$3/2093631819 无法转换为
* java.util.function.Consumer,除非您注释掉接口定义中的扩展子句 */
// Consumer<Student> interfacesAreStillTypes = anotherTypeOfConsumer;
/* 但这个不会抛出异常,因为它解析了它所需的一切 -> 匿名方法签名和定义... */
Consumer<Student> yetAnotherTypeOfConsumer = anotherTypeOfConsumer::consumeStudentObject;
/* 调用实现 */
anotherTypeOfConsumer.consumeStudentObject(student);
// interfacesAreStillTypes.accept(student);
yetAnotherTypeOfConsumer.accept(student);
}
}
在后一个示例中,AnotherTypeOfInterface 将具有一个名为 consumeStudentObject
的单一方法,该方法将与 Consumer::accept
匹配,但是 Consumer 实例还具有自己的成员,如 Consumer::andThen
。
英文:
DISCLAIMER: I'm sorry but I couldn't get your question, still I think I could be in favor with adding my toughts. Hopefully not so many "downs" for it
It's all about perspective. If I have to explain what a lambda is and how to use it, than I will use the following abstraction:
- Functional interface:
- Single abstract method signature(stucked with public as it's an interface member -> generics, return type, argument types, throws clause ->
public <T> void processT(T t)
,public <T,R> R evalT(T t)
etc.) - Could have zero or more non abstract methods (default/static).
- Abstract method has no access to other instance members at any point!
- Lambdas:
- Pure method implementations(or as I call them anonymous method implementations for known functional interfaces). In order for compiler to recognize lambda statement as a valid one it should meet a method singature from any known functional interface at compile time(The target tape could be not annotated with @FunctionalInterface rather than having a single abstract method and being interface itself(ref for abstract classes).
Now, lets take closer look for your particular example:
Consumer<Type> -> a void method that accepts one argument(generic, specified type) and does some processing based on the input.
Let's consider your code now, and small showcases that I've added there for you.
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");
}
}
Now, lets keep digging:
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);
}
}
In the latter example, AnotherTypeOfInterface would have a single method called consumeStudentObject
which will match Consumer::accept
yet, Consumer instances comes with their own set of members, like Consumer::andThen
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论