-
Notifications
You must be signed in to change notification settings - Fork 3
Double Colon Operator
The double colon operator ::
was introduced to address a longstanding glitch in the FreeMarker Template Language (FTL).
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.