January 15, 2008 — Mario Gleichmann

As hinted in my last post about literals in Java, we’ve only scratched the surface on that issue.

In this entry it get’s even weirder, since i will show you a very unconventional way of how to declare Maps in a more literal form. Maps are surely and undisputed one of the most used examples when it comes to show how verbose Java is, comparing to such hip languages like Ruby or Scala.

Don’t get me wrong – it’s definitely worth to ‘play’ with such languages – beside the often mentioned widening of your mental ken (and of course getting a clue of how concise and smooth a language might be), it may incites you on the other hand to grab some of their ideas and concepts and experiment and play with them in your ‘home language’ (yes, i have to come out as one of those dumb java guys).

In that sense, consider the following sections as a result of experimenting with Java (the language) and simply put yourself in the place of McGyver. How would he meet those limited instruments of such a medieval language like Java. I’m sure he would come up with some unconventional and unusal solutions in order to get something of the sparkle and glamour of all of those funky new languages ;o)

Tuples

One concept, that unfortunately didn’t get it’s way into Java is the idea of Tuples. If you take a look at Scala, you’ll see that it provides some classes for Tuples with products up to 22 components (scala.Tuple1, …, scala.Tuple22).

So far, nothing to fear in Java. We simply could come up with some similar classes:

package types; public class Tuple1 <T1> { public T1 _1 = null; public Tuple1(T1 o1){ _1 = o1; } } ... public class Tuple5<T1,T2,T3,T4,T5> extends Tuple4<T1,T2,T3,T4>{ public T5 _5 = null; public Tuple5(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5){ super( o1, o2, o3, o4 ); _5 = o5; } } ...

Speaking about concise code, a special convenience of Scala is that it provides a literal way of declaring Tuples:

val pair = ( "a", "Tuple2" ) println( pair.getClass ) // class scala.Tuple2 val quadruple = ( 1, 2.0, 3, "four" ) println( quadruple.getClass ) // class scala.Tuple4

Now we have a problem, since Java doesn’t offer a similar way to instantiate a class (the nearest form would be the instantiation of an Array, but McGyver said that’s useless since we also want to declare typesafe Tuples with potentially different types on each position).

The o-Operator

Ruby has its spaceship-Operator, Groovy its elvis operator. To keep up with all those funky Operators we’re also gonna define now a new Operator for declaring arbitrary tuples (call it for the sake of Marketing). Well, sort of Operator, since we’ll again use some factory methods combined with static imports. We’ll name this Operator simply the o-Operator – McGyver said so, it looks like all those other groovy Operators and it’s all in all a pretty short name:

package types; public class operators { ... public static <T1,T2> Tuple2<T1,T2> o(T1 o1, T2 o2 ){ return new Tuple2<T1,T2>( o1, o2 ); } ... public static <T1,T2,T3,T4,T5> Tuple5<T1,T2,T3,T4,T5> o(T1 o1, T2 o2, T3 o3, T4 o4, T5 o5 ){ return new Tuple5<T1,T2,T3,T4,T5>( o1, o2, o3, o4, o5 ); } }

Now we can create some Tuples in a literal-like way by using our o-Operator:

import static types.operators.*; import static system.io.*; ... println( o( 1, 2.0, 3, "four" ).getClass() ); // class types.Tuple4

Maps

When declaring a Map, you always have to use put() to push some Entries into, blowing the code. Looking at Ruby (Hashes) or Groovy, there’s on both sides a nice way creating them while using more syntactic sugar:

aGroovyMap = [ 'Faust' : 'Goethe', 'Kosmos ' : 'Humboldt', 'Monadologie' : 'Leibnitz'] a_ruby_hash = { :Leviathan => "Hobes", :Staat => "Platon", :Politik => "Aristoteles" }

With our Tuples and the o-Operator, it’s not that difficult now to come up with a quite concise solution. Admitted, it’s not as short as in Groovy or Ruby, but it’s way shorter than using good old put() method for every Entry. More Readable? That’s what you have to decice (McGyver gets used to it after a short while and has no Problem to understand what’s going on).

Like in the last post, we will define a static factory method for declaring a Map that will accept an arbitrary number of Tuples with length 2. Again we play with upper and lower case usage in a rather unconventional way for class and method names to make it appear as if we would declare an instance of type Map in a literal way (like we did with Lists, Arrays and Sets in the last post):

package literal; ... public class collection { ... public static <K,V> Map<K,V> Map( Tuple2<K,V> ... entries ){ Map<K,V> map = new HashMap<K,V>(); for( Tuple2<K,V> entry : entries ){ map.put( entry._1 , entry._2 ); } return map; } }

Now we can declare a Map like so:

import static literal.collection.Map; import static types.operators.*; ... Map( o( "Faust", "Goethe"), o( "Kosmos ", "Humboldt"), o( "Monadologie", "Leibnitz" ) );

Note, that we could create a Map in this way while using every combination of types we want to. You’ll always get a typesafe Map (as long as all Tuples use the same type for all first components and another same type for all second components) since we use Generics on Tuples as well as within the factory method (in the above example we could assign the instance to a variable of type Map<String,String>)

Would you trust McGyver ?

Again you have seen, that there’s no magic at all. We simply used some of Java’s instruments like static imports, Generics and varargs in some rather unusual style (at least for the decent Java developer). Unconventional? Yes! Weird? Maybe. But having limited instruments and using those in new ways wouldn’t deter McGyver from using it. It’s the result that counts (In the next episode of McGyver, we’ll have a look about the results of some syntactic sugar for Ranges and Regexps).

Of course we could say that McGyver is a lone wolf and no other than himself have to use his constructions – and that’s a valid argument. To me, i would rather question how customization and adaption affects the way we work in the middle and long term. Of course, everything that’s unknown or unaccustomed looks odd. But if you trust the underlying mechanisms and know how thinks work, you get used to it (i’m interessted in your opinion). In our case, the underlying mechanisms are very easy to understand. Everyone of you could easily come up with a similar solution!

What’s left? I think it’s again the conclusion, that Java isn’t as inflexible as some voices complain about when it comes to reducing biolerplate code. Every language has its shortcomings (some more, some less) – but even in such (for me not the most important) fields of verbosity not all is lost. It’s also about your attitude on how to use the given instruments and making the language more comfortable for you – it’s about your McGyver attitude …