1

I want to make it a common fuction to filter a list using a java stream.
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());

2 Answers 2

2

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:

<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);
}
Sign up to request clarification or add additional context in comments.

Comments

2

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);
    }
});

1 Comment

Just to make it uglier, getDeclaredField would need a try-catch :(

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.