Saturday, September 17, 2022

Do more with less - the way to write code, what Groovy taught me . . .

I was not happy at all going back to verbose native Java, leaving my years of happy Groovy and Grails development on JVM. The shrinking market for Groovy pushed me out, but I am not away from it. I still stay with it and write Groovy code on any given day. At least on the side, when I explore and experience some of new Java language features along the way. One question always comes to my mind is - how did we do it in Groovy, why didn't we have to worry about this obvious expectations. The comparison always pleasantly proves that Groovy stood by it's literal word-meaning- very pleasant, to work with, and a very well thought-out and super consistent language on JVM right from day-1.

Environment: Java 17, Groovy 4.0.2 on macOS Catalina 10.15.7

In my recent Java code, I was dealing with a list of request objects that come in a specific order, and had to give the response objects back in the same order after doing some internal processing that included database query for the list by ids with Spring Data's findAllBy conventional interface. In between I built a map from the request list for faster lookup of specific object's request details. I wanted to retain the order in the map and then the Java's functional feature threw me out to know some internals and come back before take things granted for to use it.

The following is a simple code snippet comparing both Java and Groovy features in the same Groovy script. That's another beauty of Groovy, you can write both Java and Groovy code in one class file.

import java.util.stream.Collectors // a data item record Item(Integer id, String name){} //groovy List items = [ new Item(1, 'One'), new Item(2, 'Two'), new Item(3, 'Three'), new Item(4, 'Four'), new Item(5, 'Five'), new Item(6, 'Six'), new Item(7, 'Seven'), new Item(8, 'Eight'), new Item(9, 'Nine'), new Item(10, 'Ten'), ] println "Groovy\n======" println "Items[id:Integer, name:String]: ${items}" def itemsMap = items.collectEntries{[it.name(), it.id()]} println "Items map by name (order preserved): ${itemsMap}" println "Items map keys (order preserved): ${itemsMap.keySet()}" // java List itemsJava = List.of( new Item(1, "One"), new Item(2, "Two"), new Item(3, "Three"), new Item(4, "Four"), new Item(5, "Five"), new Item(6, "Six"), new Item(7, "Seven"), new Item(8, "Eight"), new Item(9, "Nine"), new Item(10, "Ten") ); System.out.println("Java\n===="); System.out.println("Items[id:Integer, name:String]:" + itemsJava); var itemsMapJava = itemsJava.stream() .collect( Collectors.toMap( item -> item.name, item -> item.id ) ); System.out.println("Items map by name (order NOT preserved): " + itemsMapJava); System.out.println("Items map keys (order NOT preserved): " + itemsMapJava.keySet().stream().toList()); var itemsMapOrderPreserved = itemsJava.stream() .collect( Collectors.toMap( item -> item.name, item -> item.id, (key1, key2) -> key1, // key conflict resolver LinkedHashMap::new // pass the underlying map implementation you want, to preserve the order, defaults to HashMap ) ); System.out.println("Items map by name (order preserved): " + itemsMapOrderPreserved); System.out.println("Items map keys (order preserved): " + itemsMapOrderPreserved.keySet().stream().toList());

The output:

Groovy ====== Items[id:Integer, name:String]: [Item[id=1, name=One], Item[id=2, name=Two], Item[id=3, name=Three], Item[id=4, name=Four], Item[id=5, name=Five], Item[id=6, name=Six], Item[id=7, name=Seven], Item[id=8, name=Eight], Item[id=9, name=Nine], Item[id=10, name=Ten]] Items map by name (order preserved): [One:1, Two:2, Three:3, Four:4, Five:5, Six:6, Seven:7, Eight:8, Nine:9, Ten:10] Items map keys (order preserved): [One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten] Java ==== Items[id:Integer, name:String]:[Item[id=1, name=One], Item[id=2, name=Two], Item[id=3, name=Three], Item[id=4, name=Four], Item[id=5, name=Five], Item[id=6, name=Six], Item[id=7, name=Seven], Item[id=8, name=Eight], Item[id=9, name=Nine], Item[id=10, name=Ten]] Items map by name (order NOT preserved): [Eight:8, Five:5, Six:6, One:1, Four:4, Nine:9, Seven:7, Ten:10, Two:2, Three:3] Items map keys (order NOT preserved): [Eight, Five, Six, One, Four, Nine, Seven, Ten, Two, Three] Items map by name (order preserved): [One:1, Two:2, Three:3, Four:4, Five:5, Six:6, Seven:7, Eight:8, Nine:9, Ten:10] Items map keys (order preserved): [One, Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten]

Conclusion

Java started to evolve, changing faster than it used to be and faster than the community can catch up- all for good. It's slowly getting to be less verbose. Still, certain obvious expectations are not so obvious in Java.

Groovy shined next to legacy Java, still shines next to modern Java, on the JVM.

No comments:

Post a Comment