Search code examples
javagroovyjava-stream

How to call Java's Stream.collect() from groovy?


Having some background with FP from Scala I really don't like Groovy's names for collections methods. Given that and some architectural choices made above, I found using a Java 8 streams API (plus java.util.Optional) in Groovy code an appealing solution.

Until I hit this:

def finalCollection = [ 'some', 'items', 'really', 'not', 'important' ].stream()
        .map { aMethodReturningOptional(it) } //map String to Optional<Item>
        .flatMap { it.map(Stream.&of).orElseGet(Stream.&empty) } //convert to Stream<Item>
        .collect() //Groovy's collect, not stream's!

Note it works only in Groovy 2+ - treating closure as lambda. What bothers me is the last line of the example code. Groovy translates the call to the DefaultGroovyMethods.collect() instead of Stream.collect() that I originally wanted to use. Of course then the last line would be:

        .collect(Collectors.toList()) //Should call Java collect, but it doesn't

It seems counter-intuitive to me, that some extension method is called instead of native, 'built-in' method of the class.

How can I rewrite the example so the Stream.collect() method would be called?

UPDATE: After some more fiddling, I've found out what problem I had originally. I wrote .collect{Collectors.toList()} (note curly braces), which of course called Groovy method, not Java. Note to self: remember to quadruple-check before posting...


Solution

  • Using Collectors.toList() you can get what you want to do:

    import java.util.stream.*
    
    class Item {
        final String name
    
        Item(name) {
            this.name = name
        }
    
        @Override
        String toString() {
            name
        }
    }
    
    def itemize(String name) {
        Optional.of(new Item(name))
    }
    
    
    def finalCollection = [ 'some', 'items', 'really', 'not', 'important' ].stream()
            .map { itemize(it) } //map String to Optional<Item>
            .flatMap { it.map(Stream.&of).orElseGet(Stream.&empty) } //convert to Stream<Item>
            .collect (Collectors.toList()) 
    
    assert 'java.util.ArrayList' == finalCollection.class.name        
    assert finalCollection.collect { it.name } == ['some', 'items', 'really', 'not', 'important']
    

    Anyway, with groovy 2.4.5 the above is working also with

    def finalCollection = [ 'some', 'items', 'really', 'not', 'important' ].stream()
            .map { itemize(it) } //map String to Optional<Item>
            .flatMap { it.map(Stream.&of).orElseGet(Stream.&empty) } //convert to Stream<Item>
            .collect()
    

    that uses groovy's collect:

    transforming each item into a new value using Closure.IDENTITY as a transformer, basically returning a list of items copied from the original object.