Building your own literals in Java – Tuples and Maps

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 …

About these ads
Posted in fun, java. 13 Comments »

13 Responses to “Building your own literals in Java – Tuples and Maps”

  1. Stephan Schmidt Says:

    Very nice post, I also think a lot about how to make Java more readable.

    Peace
    -stephan

  2. ted stockwell Says:

    I *really* like how you’ve used static imports to essentially create your own DSL. Java’s still got *it*. I wish I had though of it first :-).

    PS: Instead of ‘good old push() method for every Entry’ I think you meant ‘good old put() method for every Entry’.

  3. frmad Says:

    This post is very hot, it is high ranked at our site (daily weblog, weblog post ranking site). See http://indirect2.blogspot.com/ for more infomation

  4. Mike Desjardins Says:

    Very cool post. I’ve been learning Python lately, and tuples are pretty cool. I have never even considered finding a way to bring them to Java – this is brilliant!

  5. Mario Gleichmann Says:

    Thanks Stephan,

    good to hear about other people who see a ‘future’ for Java even in the field of readability :o)

    You surely have read ‘Implementation Patterns’ where Kent is ruminating about the value of communication. I think there are pretty much options left in Java to increase readability if we only combine the given instruments …

  6. Mario Gleichmann Says:

    Ted,

    thank you very much for your hint – i replaced the wrong method name in the post.

    I’m sure there are more than one developer out there (including you) who had already similar ideas. It’s all about playing and experimenting with the given features …

  7. Mario Gleichmann Says:

    Mike,

    thanks for your feedback.

    I have to admit – if i hadn’t played with Scala then i even hadn’t thought about bringing something similar like Tuples to Java.

    It’s another fine example of how dealing with different languages can rise your horizon of experience, giving you some new ideas in the sense of ‘hm, what if i would …’
    I’m very sure you had similar ideas when learning Python.

  8. Declarative programming: a Range type for Java « brain driven development Says:

    [...] In order to create a Range in a more declarative style, we’ll again put a simple factory method to our collection class (see the previous posts): [...]

  9. Reinier Zwitserloot Says:

    I found this via via. I discovered this hack a few weeks ago, but it doesn’t actually ‘work’, at least according to my definition.

    Any attempt to make a map literal with this structure will always generate a generics warning. The warning is just retarded but so far my attempts to get this bug fixed in javac have met with insistence that the warning makes sense. A shame. Have you found any way to get around it?

    NB: The warning is about the creation of an array with generics parameters. I consider it a stupid error because if anyone needs to be warned about the peculiarities of that, it’s the method with the varargs on it, not the code that calls it.

  10. AA Says:

    I found this via Stackoverflow.

    I do love static import as well. In more conservative environments (ie when teaching) I use a class called `As` such that I can use As.list(…) and As.map(…) et cetera

  11. EdwinF Says:

    Well, I would have done the Tuple class in other way… (no compiler at hand, sorry if it’s broken, but you’ll get the idea)

    class final Tuple 
    {
       private Object[] container;
       private Tuple(final Object ... type){
         container = type;
      }
    
      public static Tuple tuple(final Object ... type) {
        return new Tuple(type);
      }
    }
    
    ...
    import static bla.Tuple.*;
    ....
    final Tuple t = tuple(1,2.3,4,"blah");
    
    • Mario Gleichmann Says:

      Thanks for your feedback, Edwin.

      Just two points, answering your proposal.

      1. Type safety – if you want to retrieve the first element, say in a pair instance (Tuple2), you’ve lost type information, since all of your elements in the container are of type Object. So at any time, you need to know the type of your elemens and do casting if you want to refer to it.

      2. ArrayIndexOutOfBounds – you never know (again as a client or ‘reader’ of a tuple), if you have a Tuple with 2 or 5 or 10 elements. Refering to a non existent element would result in an ArrayIndexOutOfBoundsException

      That said, you could also directly go with an Array of Objects instead of wrapping it with a Tuple class, since there’s no additional value added.

      Greetings

      Mario

  12. Visus Says:

    I found that using the code will generate a Warning saying: “Type safety : A generic array of Tuple2 is created for a varargs parameter”, which could be annoying sometimes. How can i get rid of it?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 43 other followers

%d bloggers like this: