如何在Java流(Java stream)中的Lambda表达式中使用反射?

huangapple go评论85阅读模式
英文:

How can I use reflection in the Lambda expression in Java stream?

问题

我想将其作为一个常见函数,使用Java Stream来过滤列表。
<br> 但它的工作方式不符合我的预期...

以下代码无法正常工作,但是否可能在lambda表达式内部使用反射进行过滤?

 List filterList = commonList.stream()
                .filter(x -&gt; x.getClass().getDeclaredField(&quot;metadata&quot;).get(this)
                .getClass().getDeclaredField(&quot;name&quot;).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 -&gt; x.getClass().getDeclaredField(&quot;metadata&quot;).get(this)
                .getClass().getDeclaredField(&quot;name&quot;).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&#39;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:

  1. Lambdas may not throw checked exceptions and reflective methods throw them
  2. 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:

&lt;T&gt; 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 -&gt; this.&lt;String&gt;getField(&quot;name&quot;, getField(&quot;metadata&quot;, x)).matches(keywords))
    .collect(Collectors.toList());

The easy way to code the method is using Lombok:

@SneakyThrows
&lt;T&gt; getField(String fieldName, Object obj) {
    return (T)obj.getClass().getDeclaredField(fieldName).get(obj);
}

huangapple
  • 本文由 发表于 2020年10月25日 20:21:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/64523619.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定