Brainteaser: Why does this line of Velocity code *work*?
My favorite part of Velocity training sessions — though surely students’ least favorite! — is challenging someone to explain why something worked, as opposed to why something fails fatally.
It’s easy to barrel through Velocity without realizing the complex stuff happening under the hood. If Velocity doesn’t scream when you call a certain method, you don’t think twice about why it works, you’re just happy it did.
But when you pay attention to Velocity/Java datatypes (as you must if you’re going to write reliable code) and double-check the Java docs, things can seem... kinda screwy. Like they shouldn’t work, even though they do. But why?
For example...
Take a String field Sports Interests. Populated via a Checkboxes field on a form, it holds a semicolon-delimited list. Here’s the value for our test lead:
Our upcoming webinar features a special guest appearance by a beach volleyball legend, so we’re going to add special content for people we know are fans:
#set( $SportsList = $lead.SportsInterests.split(";") )
#if( $SportsList.contains("beach volleyball") )
If you don't know Karch Kiraly, you don’t know Beach!
#end
And it works!
We split
the String field on the known delimiter ;
to create a list (or, shall we say, a list-like object) where each sport is an item in the list.
Then check if the list contains
the interesting sport.
But here’s the thing. $SportsList
isn’t a capital-L List. It’s actually an Array, which is starkly different in Java. Here’s the output of ${SportsList.class}
:
[Ljava.lang.String;
In Java-speak, the [
prefix means an Array. L
introduces the name of a class. java.lang.String
is the built-in String class.
So [Ljava.lang.String;
means simply “an Array of (only) Strings.” (In Java, every item in an Array must be of the same type, which is unlike many other languages’ concept of an “Array.”)
A Java Array is simply a high-performance*, suitably primitive, ordered container of things of the same type. You can seek the [0]
-th or [99]
-th item in an Array wicked fast. But Arrays don’t have methods like add
and remove
and, uh, contains
.
Yep. Arrays don’t have contains
. But you just called contains
on an Array... or so it certainly seemed.
To be sure, an actual List object has contains
. But again, this isn’t a List. If it were a List, then when you dumped ${SportsList.class}
, you’d see java.util.ArrayList
or one of the other types of List.
Looking at the Java docs for String.split()
it’s clear it returns an Array of Strings ([]String
is the same as [Ljava.lang.String;
). So things are working as expected there. $SportsList
should indeed be the Array it seems to be. But why can you still successfully call contains
on it?
And make sure your answer also covers another case. Why can’t we do the following without throwing a fatal error?
#set( $void = $SportsList.add("football") )
Leave your answers in the comments (if you don’t want to reveal yourself, you can email me or PM me on Marketo Nation). I’ll post the answer in a few days!
Update 2021-03-04: the answer is detailed in this separate post.
Notes
* Theoretical performance differences are meaningless when you’re dealing with a few items through thousands of items.