4

Java 8.

Converting a forEach loop into a lambda expression is understood, at least for me. Converting a for loop based with a condition, in the other hand, isn't.

If it's possible ,my question would be: how can you convert the following for loop into a lambda expression

List<Field> fields = new LinkedList<>();
        for (Class<?> c = this.getClass(); c != null; c = c.getSuperclass())
            Collections.addAll(fields, c.getDeclaredFields());

Many thanks in advance,

~Ben.

Benma
  • 105
  • 1
  • 9
  • Lambda isn't going to help you here. If you want to get all fields of class including inherited, try this solution: http://stackoverflow.com/questions/1042798/retrieving-the-inherited-attribute-names-values-using-java-reflection – esin88 Mar 01 '17 at 09:46
  • I disagree with the accepted answer that recursion is the better approach, second my inductive code works better. – Benma Mar 01 '17 at 09:50
  • `Stream` s direct replacement for **for-each** style. But here, you can't either flatten the stream, what you need is iterate over the stream by yourself. Which means, internal iteration will not help you. – Jude Niroshan Mar 01 '17 at 09:51
  • I would love to know if my question is even possible – Benma Mar 01 '17 at 09:52
  • Yes, you can alternatively use `while` loop, while `c.getSuperclass()` is not null. But anyway streams and lambdas are not going to help you here. – esin88 Mar 01 '17 at 09:53
  • I would like to focus more on functional programming @esin88 – Benma Mar 01 '17 at 09:54
  • 2
    Related: [Streaming a class hierarchy](http://stackoverflow.com/q/40240450/2711488) – Holger Mar 01 '17 at 12:51

2 Answers2

5

Well, there is a way, but it needs takeWhile that it is in jdk-9.

I'm doing a mapping here to get the names of the fields. You would have to add a @SuppressWarnings("null") to the method.

System.out.println(Stream.iterate(this.getClass(), (Class<?> x) -> x.getSuperclass())
            .takeWhile(x -> x != null)
            .flatMap(c -> Arrays.stream(c.getDeclaredFields()))
            .map(c -> c.getName())
            .collect(Collectors.toList()));

jdk-9 also introduces a Stream.iterate that acts like an Iterator with seed, hasNext, next that is far more suited for your case.

You could use StreamEx library for this btw:

 StreamEx.of(Stream.iterate(this.getClass(), (Class<?> x) -> x.getSuperclass()))
            .takeWhile(x -> x != null)
            .flatMap(c -> Arrays.stream(c.getDeclaredFields()))
            .map(c -> c.getName())
            .collect(Collectors.toList());

And with new iterate method:

 Stream.iterate(this.getClass(), c -> c != null, (Class<?> c) -> c.getSuperclass())
            .flatMap(c -> Arrays.stream(c.getDeclaredFields()))
            .map(c -> c.getName())
            .collect(Collectors.toList())
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • Stream.iterate(this.getClass(), c -> c != null, (Class> c) -> c.getSuperclass()) .flatMap(c -> Arrays.stream(c.getDeclaredFields())) .map(c -> c.getName()) .collect(Collectors.toList()) – Benma Mar 01 '17 at 11:41
  • is it only possible with java9 ? – Benma Mar 01 '17 at 11:45
  • 2
    @Benma you need `takeWhile` for it to work in jdk-8. This is either in `StreamEx` library or you can add it yourself until available from here: http://stackoverflow.com/questions/20746429/limit-a-stream-by-a-predicate – Eugene Mar 01 '17 at 11:47
  • 1
    @Roland indeed, a Stream out of the Iterator. – Eugene Mar 01 '17 at 12:35
0

You maybe want to work with streams. Open a stream on your list and simply use filter. Filter takes a FunctionalInterface/Lambda-expression.

Koer
  • 1
  • 2
  • I'm well familiar with streams and filters, but to implement the following loop condition with these methods isn't straightforward to me for some reason, is there any chance for a code snippet? – Benma Mar 01 '17 at 09:49