英文:
How can I use reflection in the Lambda expression in Java stream?
问题
我想将其作为一个常见函数,使用Java Stream来过滤列表。
<br> 但它的工作方式不符合我的预期...
以下代码无法正常工作,但是否可能在lambda表达式内部使用反射进行过滤?
List filterList = commonList.stream()
.filter(x -> x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this).matches(keywords))
.collect(Collectors.toList());
英文:
I want to make it a common fuction to filter a list using a java stream.
<br> but it doesn't work out the way I want it to...
The code below doesn't work, but is it possible to filter using reflection within a lambda expression?
List filterList = commonList.stream()
.filter(x -> x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this).matches(keywords))
.collect(Collectors.toList());
答案1
得分: 2
这个表达式:
x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this)
返回一个 `Object`。在 `Object` 类中没有声明 `matches` 方法。想象一下将你的 lambda 重写为:
```java
filter(x -> {
Object y = x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this);
return y.matches(keywords);
});
因此出现了编译错误。
另外,getDeclaredField
方法可能会抛出已检查异常,因此它应该在 lambda 代码内部进行处理:
filter(x -> {
try {
return x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this).matches(keywords);
} catch (NoSuchFieldException e) {
// 在这里处理或重新抛出异常
throw new RuntimeException("过滤流时出错", e);
}
});
<details>
<summary>英文:</summary>
This expression:
x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this)
returns an `Object`. There's no `matches` method declared in `Object` class. Imagine rewriting your lambda as:
filter(x -> {
Object y = x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this);
return y.matches(keywords);
});
Thus the compilation error.
Also, `getDeclaredField` method can throw a checked exception, so it should be handled inside the lambda code:
filter(x -> {
try {
return x.getClass().getDeclaredField("metadata").get(this)
.getClass().getDeclaredField("name").get(this).matches(keywords);
} catch (NoSuchFieldException e) {
//handle it here or re throw
throw new RuntimeException("Error filtering the stream", e);
}
});
</details>
# 答案2
**得分**: 2
你有两个基本问题:
1. Lambdas可能不会抛出已检查异常,而反射方法会抛出这些异常。
2. 反射方法没有类型;它们的返回类型是 `Object`。
但是,你可以通过使用一个捕获并重新抛出为 RuntimeException 的有类型方法来解决这两个问题:
```java
<T> T getField(String fieldName, Object obj) {
try {
return (T)obj.getClass().getDeclaredField(fieldName).get(obj);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
然后,使用有类型的调用来告诉编译器 name
是一个 String
,像这样:
List filterList = commonList.stream()
.filter(x -> this.<String>getField("name", getField("metadata", x)).matches(keywords))
.collect(Collectors.toList());
编写该方法的简单方式是使用 Lombok:
@SneakyThrows
<T> getField(String fieldName, Object obj) {
return (T)obj.getClass().getDeclaredField(fieldName).get(obj);
}
英文:
You have 2 basic problems:
- Lambdas may not throw checked exceptions and reflective methods throw them
- Reflective methods are not typed; they have a return type of
Object
You can however address both problems by employing a typed method that catches and rethrows as RuntimeException:
<T> getField(String fieldName, Object obj) {
try {
return (T)obj.getClass().getDeclaredField(fieldName).get(obj);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
Then use a typed call to tell the compiler that name
is a String
, like this:
List filterList = commonList.stream()
.filter(x -> this.<String>getField("name", getField("metadata", x)).matches(keywords))
.collect(Collectors.toList());
The easy way to code the method is using Lombok:
@SneakyThrows
<T> getField(String fieldName, Object obj) {
return (T)obj.getClass().getDeclaredField(fieldName).get(obj);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论