I've been tooling around on Codewars lately, solving challenges as part of morning routine to awaken my brain and sharpen my problem-solving skills. I decided to try my hand at some Java again, and boy, was I in for a rude surprise.

Primarily I solve problems in Ruby or JavaScript, my two primary languages at the moment. For funsies and a little bit of a challenge, I decided to take on a couple easy katas in a different language. Apparently, because I'm a glutton for punishment, I chose to solve it in Java.

The challenge was straightforward: take a string of words, and print the words in reverse; so Hello I like Ruby becomes Ruby like I Hello.

Easy enough, I thought. In Ruby it's the most terse:

str.split(' ').reverse.join(' ')

JavaScript is not far behind. Unfortunately, its Array#reverse method is destructive, reversing the elements in place, but whatever JS you made some poor choices back in the day, we all learned better since then.

str.split(' ').reverse().join(' ')

Then, I took on Java. Boy, was I in for a surprise. I thought it would be very similar, pretty straightforward.

  1. Turn your string into an array of strings.

Is it str.split(" ")? Nope. Missing a semicolon. Okay fine, how about return str.split(" ");? Sure, that gives us String[] but it turns out that's a comically under-featured abstraction. Want to do something like a reversal? Good luck doing that with Anything[] in a declarative way. You want a List, which you get with Arrays.asList(thing), and also requires an import. Great! I have a solution!

import java.util.*;

public class ReverseWords {
    public static String reverseWords(String str) {
        return Arrays.asList(str.split(" ")).reverse().join(" ");
    }
}

Nope. Hold on cowboy. You want a reverse method on List? What kind of craziness is that? You need the Collections framework, which gives you Collections.reverse(list).

Okay, fine, whatever, here we go:

import java.util.*;

public class ReverseWords {
    public static String reverseWords(String str) {
        Collections.reverse(Arrays.asList(str.split(" "))).join(" ");
    }
}

Still nope?! It turns out Collections.reverse doesn't return anything which is not intuitive and only does the reversal in place.

And guess what? List doesn't have a join method either. Want to join a list back into a string? Why, that would be on the String class, of course. Why? Because Java. Doesn't. Make. Sense.

import java.util.*;

public class ReverseWords {
    public static String reverseWords(String str) {
        List words = Arrays.asList(str.split(" "));
        Collections.reverse(words);
        return String.join(" ", words);
    }
}

And there you have a working solution. This solution also happens to be highest ranked for cleverness and best practices in Codewars. This is the best that can be done in Java? Yep, and what's more, it can get a lot worse:

public class ReverseWords{
   public static String reverseWords(String str){
       String[] str2 = str.split(" ");
       String fin = "";
       for(int i=str2.length-1;i>=0;i--){
           fin += str2[i];
           if(i>0)fin+=" ";
       }
       return fin;
   }
}

There is no way you could, at a glance while squinting, understand what this code is doing (without reading the method name). I like to ask myself: does my code pass the "squint test"? (It's often used in reference to parsing graphs, but I think it applies to software as well.) I focus on writing code that can be skimmed so that later, when I've lost all context about the code I wrote, I don't get annoyed with past Andrew.

I remember being similarly frustrated by the limitations of Java when I was working on the backend of Kinesis Analytics at AWS. Doing a basic operation such as mapping across an array ( arr.map { |x| doSomething(x) } in Ruby or arr.map(x => doSomething(x)) in JS) in Java requires a baffling amount of verbosity:

arr.stream().map(e -> doSomething(e)).collect(Collectors.toList());

Moments like these make me appreciate how languages like Ruby were so revolutionary, in focusing on using human-readable syntax and favoring declarative over imperative coding. The Java solution reveals so many more implementation details and requires far more constructs ( String, String[], List<String>, Collections).

It's not about brevity or fewest keystrokes (though I do enjoy a clever solution in katas), it's about expressiveness. I value the ability to express in abstract, high-level terms about something I want to accomplish. That's the point of declarative programming: focus on what kinds of transformations you want to perform on your data, rather than how to perform those transformations.