Skip to content
Print

Java Needs Map Syntax Sugar

18
Dec
2007

I’ve never been a big fan of useless syntax sugar.  I’ve always thought that the simpler a language is syntactically, the better.  If you think about it, there’s a lot of merit to this sort of viewpoint.  I mean, which of these is easier to read?

public class Test {
    public void put(Object key, Object value) {
        // ...
    }
 
    public Object get(Object key) {
        // ...
    }
}
 
// ...
Test test = new Test();
test.put(String.class, "blah");
test.put(StringBuilder.class, "blahblah");
 
String value = (String) test.get(StringBuilder.class);

Or this?

public class Test<K, V> {
    public void put(K key, V value) {
        // ...
    }
 
    public V get(K key) {
        // ...
    }
}
 
// ...
Test<Class<? extends CharSequence>, String> test = 
        new Test<Class<? extends CharSequence>, String>();
test.put(String.class, "blah");
test.put(StringBuilder.class, "blahblah");
 
String value = test.get(StringBuilder.class);

The only thing that has different between the examples is one makes use of generics, the other doesn’t.  The top example makes use of a cast, the bottom avoids this.  The lower example also has some trivial compile-time checking, but that’s about it. 

So what have we done here?  We complicated a trivial example with 50-100 extra, cryptic characters and managed to avoid a single, 9-character cast.  Not really an improvement.  I’ll grant you that generics are usually very beneficial, and I won’t deny that I like them and make use of them myself, but the point of the example is unchanged: extra “beneficial” syntax constructs are not always a good thing.

With all this said, I still have to admit that I’m (grudgingly) in favor of certain syntax additions.  Closures for example are probably the absolute best proposal for Java 7 I’ve heard yet.  Obviously we can quibble over the syntax, but I think the construct itself is a very valuable one.  I’m also (again grudgingly) in favor of another syntax sugar having to do with method parameters.

Many methods these days take a Map as a parameter.  It’s a fairly common thing to pass values that way.  JDBC is a good example (well, actually it’s a Properties instance).  ActiveObjects has a method to create an entity, passing a map of field => value pairs to be set on the INSERT.  Often times, such methods are an enormous pain to call.  Example:

Map<String, Object> params = new HashMap<String, Object>();
params.put("firstName", "Daniel");
params.put("lastName", "Spiewak");
Person person = em.create(Person.class, params);

Alright, not horrible. But imagine you had several dozen values to pass. It’s not that uncommon for such a situation to arise, and right now it always leads to vast quantities of syntax cruft.

Of course, there is a shortcut notation, but it’s almost as verbose and virtually unknown (which makes it a bad choice due to readability concerns):

Person person = em.create(Person.class, new HashMap<String, Object>() {{
    put("firstName", "Daniel");
    put("lastName", "Daniel");
}});

What this is actually doing is declaring a constructor for an anonymous inner class extending HashMap.  It’s not too bad in terms of syntax, but very few Java developers are aware of this construct, thus it should probably be avoided.

Now if we consider the corresponding syntax in a language like Ruby, things fall out beautifully (here using symbols instead of Strings for the keys):

person = em.create(Person, :firstName => "Daniel", :lastName => "Spiewak")

Obviously I’m taking a bit of artistic license with the API, but you get my drift.  This code is so much cleaner because Ruby has a syntax construct which allows the implicit creation of Hash objects (the Ruby equivalent of Map) as required within the method invocation.  The above syntax is equivalent to the following:

params = Hash.new
params[:firstName] = "Daniel"
params[:lastName] = "Spiewak"
person = em.create(Person, params)

So nothing too complex, Ruby just makes it cleaner.  Most scripting languages actually have a similar construct, for example Groovy allows for the key:value syntax in method parameters, dynamically creating a Java Map as necessary.

I think Java should add a similar syntax.  There are enough cases where such a construct would greatly simplify the code in question.  We wouldn’t even need to add symbol literals to the language, we could just use standard Java types.  Perhaps something like the following is in order:

Person person = em.create(Person.class, "firstName":"daniel", "lastName":"Spiewak");

Completely unobtrusive, easy to read, and even more compact than the Ruby version.  The syntax is familiar to anyone using Groovy (more and more these days) and it doesn’t require the introduction of a new keyword or clumsy over-riding construct.  It’s type-checked, so compile-time safe, and totally backwards compatible (you could use this syntax on legacy APIs without modification).

So the question: is it worth it?  Since Java was open sourced, we’ve been seeing more and more efforts like KSL (the Kitchen Sink Language).  People have been hearing so many new language proposals that there’s beginning to be an anti-change backlash.  Many of the developers I respect are more and more of the opinion that Java should remain the way it is.  Those who want all the fancy language additions should use Scala.

In a lot of ways, I agree with them.  In my opinion, Java’s too bulky and inconsistent in its syntax already, but I think this might just be one area where an exception could be made.  Perhaps this construct is simple enough, unobtrusive enough and ubiquitously useful enough to be worth the effort of putting it into the language.

Comments

  1. I don’t think Java “needs” it, but I agree: Java would benefit from some syntactic sugar around HashMap. It’s not without precedence; consider the syntactic sugar around String.

    Ruby’s hash syntax is excellent, and greatly influences APIs built with Ruby, including many of the Rails APIs. The simple hash syntax basically gives you a simple form of named arguments, which can be used to make a very powerful, but easy-to-use, API, because you don’t have to pass arguments in a specific order, and you can better grok a method call without looking up its definition.

    Of course, closures have an even bigger impact on APIs. :)

    Even the following example, which is a bit less of a leap from Java in its current form, would be a big improvement:

    Person person = em.create(Person.class, new HashMap(“firstName”:”daniel”, “lastName”:”Spiewak”));

    …Although I think I prefer Ruby’s => notation to the colon notation you’ve borrowed from Groovy.

    Jeremy Weiskotten Tuesday, December 18, 2007 at 1:05 pm
  2. if you want dynamic operations like key:value assignment, use groovy, else use Java
    you can use them together, all groovy objects are Java objects too.
    You can use also the JavaFX Script and it’s structured style code, it’s also same as groovy, JavaFX’s objects are Java objects too.

    Nir Tayeb Tuesday, December 18, 2007 at 1:55 pm
  3. What this is actually doing is declaring a constructor for an anonymous inner class extending HashMap.

    A InstanceInitializer isn’t a Constructor. http://java.sun.com/docs/books/jls/third_edition/html/classes.html#246032

    And I wouldn’t call it a shorthand notation it does what you declare it to do in the order that you declare it should be done and there is no long form that is truely equal. (sure there are ways that the result will be the same but the execution is/operations are different.) (step 4 in paragraph 12.5 for execution order) (For the ppl that had there cup of coffee: pls note how if there wouldn’t be subclass beeing created here you would get a runtime error ;-)

    to dive into the actual discussion; I wouldn’t call the ruby bit cleaner, shorter? yes, less effort? yes, easier to read? if you say so. Sure the java syntax is a bit painful I agree. Also the problem isn’t limited to maps alone. Think other containers like (J)Component’s but also simple Lists. Now one option for that was to make add() methods return the object your adding to allowing for:

    Container container = new Container();
    container.add(foo)
    .add(bar)
    .add(ducky)
    .add(firetruck);

    (someone even suggested that void could be implicitly return the object which the method belongs to)
    But any add()-alike method might return at this time something else like the status of the operation or other stuff. But it falls more into api-design then language-design I guess.

    you asking for a (shorthand) syntax to create Map.Entry and implicitly wrap those in a Map which is where the problems start. The later(implicitly wrapping) creates unreadable bit’s of code. What if em.create() takes 3 arguments Type Map and something else(heaven forbit: a Map). Sure you can get around that by using the same restrictions as Object… object has. but that has it’s drawbacks too.

    now you can hope for something alike (please ignore that Map is an interface, I suppose you could use Unmodifiable map)
    Person person = em.create(Person.class, new Map(“firstName”:”daniel”, “lastName”:”Spiewak”));
    Person person = em.create(Person.class, Collections.unmodifiableMap(“firstName”:”daniel”, “lastName”:”Spiewak”));
    I gues you see the drawbacks of that too.

    JDBC is low level, also in general java developers don’t like collections with unenforced rules to masquerade as types. Which is where script-like languages and java-like languages clash. Sure creating shorthand constructions for data objects is tedious. Illustration:

    Person person = new Person();
    person.setFirstName(“Michael”);
    person.setLastName(“B”);

    person = em.persist(person);

    vs

    Person person = new Person(“Michael”,”b”);
    person = em.persist(person);

    (+ creating dull constructors)
    causes problems if ’shorthand’ constructors end up having the same type arguments.

    Here ‘named parameters’-constructors for beans that can be and should be auto-generated could be a blessing:

    (Person is a bean)
    Person person = new Person(FirstName:”Michael”,LastName:”B”);
    person = em.persist(person);

    Person person = new Person(LastName:”B”,Foo:”bar”);
    person = em.persist(person);

    Some other notes:
    I wouldn’t know about inconsistency but if you referencing to verbose as bulky then I like bulky and it would be intentionally bulky.

    Also generics was introduced to improve type safety not as syntax sugar.

    Michael B Tuesday, December 18, 2007 at 3:48 pm
  4. If you want to implement it in javac, the Kijaro project is the place to go, not the KSL – https://kijaro.dev.java.net/

    Stephen Colebourne Tuesday, December 18, 2007 at 4:25 pm
  5. I can’t disagree more.

    >Many methods these days take a Map as a parameter

    Well, I’d call an API with such methods a bad API. Why not defined some constants while you’re at it:

    public static final String FIRST_NAME = “firstName”;

    Bah.

    >Person person = em.create(Person.class, “firstName”:”daniel”, “lastName”:”Spiewak”);

    What if the create method takes two maps as arguments create(Class clazz, Map mapA, Map mapB)?
    I don’t know how Ruby does it but how should Java be able to sort out which values belong to which parameter?

    svenmeier Wednesday, December 19, 2007 at 5:55 am
  6. In such a case where there is ambiguity (such as the two Map instance), the syntax could only be used for the latter Map. This isn’t entirely without precedence in Java, where we have some nice(?) syntactic sugar for varargs, but only on the last parameter of the method.

    Daniel Spiewak Wednesday, December 19, 2007 at 12:34 pm
  7. See

    http://gleichmann.wordpress.com/2008/01/15/building-your-own-literals-in-java-tuples-and-maps/

    Peace
    -stephan

    Stephan Schmidt Tuesday, January 22, 2008 at 12:38 am
  8. Just create class:
    public class SSMap extends HashMap {

    private static final long serialVersionUID = 1L;

    public SSMap i(String key, String value) {
    this.put(key, value);
    return this;
    }

    }

    and then:

    SSMap params = new SSMap()
    .i(“idSite”, “1″)
    .i(“period”, “day”)
    .i(“date”, “today”);

    Peter Friday, February 5, 2010 at 1:41 am

Post a Comment

Comments are automatically formatted. Markup are either stripped or will cause large blocks of text to be eaten, depending on the phase of the moon. Code snippets should be wrapped in <pre>...</pre> tags. Indentation within pre tags will be preserved, and most instances of "<" and ">" will work without a problem.

Please note that first-time commenters are moderated, so don't panic if your comment doesn't appear immediately.

*
*