Skip to content

Double Colon Operator

Jonathan Revusky edited this page Feb 15, 2024 · 6 revisions

The double colon operator :: was introduced to address a longstanding glitch in the FreeMarker Template Language (FTL).

The Basic Motivation

When you write foo.bar and foo is a map of some sort, usually this maps onto the Java code foo.get("bar"). If foo is a regular POJO (plain old Java object) foo.bar could be taken to mean foo.getBar(). However, also foo.bar could refer to the bar() method (assuming it exists, of course) in the Java class.

Now, one thing to note about this is that this is an ambiguity that does not exist in the Java language itself. A reference to a Java method, like foo.bar() and a field foo.bar is disambiguated by the presence (or not) of the parentheses. And this is actually resolved at compile-time. FreeMarker does not work like that. For one thing, because there is no "compile-time"! Consider:

#var macroLookupTable = {"get" : SomeMacro, "set" : AnotherMacro ...}

So a call to macroLookupTable.get(...) could refer to the get() method in the core java.util.Map API Or it could refer to the macro SomeMacro mapped to the get key. You might not think this happens too often, but it is a basic glitch in the language definition. If somebody writes:

 macroLookupTable.get("foo")

should that translate to a lookup in the underlying java HashMap using the get() method or should it result in a call to SomeMacro("foo")? Probably the intention is the latter, but there is a real ambiguity there which, at the very least, is kind of annoying conceptually.

Finally, FreeMarker 3 addresses this by requiring you to use :: when calling Java methods from the template layer. Thus, something like:

   myString.toUpperCase()

must now be written:

   myString::toUpperCase()

Well, except there is an exception to this, which the more astute reader will likely have anticipated. If you set #ftl legacy_syntax at the top of your template, you can still write it the older way with the ..

Nonetheless, I (revusky) had some hesitancy about the above-described change, but finally I reasoned that there was actually some advantage in making it totally clear visually in the template which method calls were direct calls into the Java layer, as opposed to calls to functions/macros defined in the FTL layer. Typically, when reading code, it is very nice to be able to find where something is defined without too much fuss, so having it be extra clear that you are calling a Java method could definitely have some advantages.

I suppose the main disadvantage is that the notation is novel. (Though, actually, not quite, because since JDK 8, Java does use the :: notation to define MethodReference objects, which are really lambdas actually, not quite the same thing, but at least the notation is the same.)

In my own work, I actually did not find it very difficult to go through the various templates and replace . with ::. And the resulting templates did not seem to be any less readable. I still concede that people may find the new syntax a bit jarring at first, but I have to think that people have found the ? built-in syntax jarring at first sight. What happens then, I think, is that people just get used to it.