Tales from the jar side: Records, JSON, and Spring, A take on Elon Musk, My chess weekend, and Good tweets
Me: The problem with the world is that idiots are so sure of themselves, and intelligent people so full of doubts. You: Do you think that's true? Me: Absolutely!
Welcome, fellow jarheads, to Tales from the jar side, the Kousen IT newsletter, for the week of April 24 - May 1, 2022. This week I taught a week-long Java and Spring course for a private client composed of Ruby on Rails and C# developers.
Regular readers of this newsletter are affectionately known as jarheads, and are far more intelligent, sophisticated, and attractive than the average newsletter reader. If you wish to become a jarhead, please subscribe using this button:
As a reminder, this message is truncated in email, click on the title at the top to open it in a browser tab formatted properly for both the web and mobile.
I’m a bit late this week because of the Golden State Warriors game 1 of their series against the Memphis Grizzlies, which just ended. If you recall last week’s newsletter, I lamented the fact that Steph Curry, officially the Greatest Free Throw Shooter In NBA History, missed four (!) free throws. In the clinching Game 6 of that series against Denver, he missed two more (??). It today’s series opener against Memphis, he missed another one. But then his fellow All Star, Klay Thompson, hit a clutch 3 pointer to take the lead with less than 10 seconds to go, then, after he got fouled, MISSED BOTH FREE THROWS. I mean, WTF? Klay is a career 91% FT shooter. Ugh.
And yet, they won. I’m recovering, but let’s move on to other things.
This first section is the technical stuff, which you can skip if you’re not into that.
Records, JSON, and Spring
My class this week used Java 17, the latest Long Term Support (LTS) version of Java. That’s not very common in the industry yet. As a matter of fact, New Relic released the results of their latest developer survey this week as well. The report stated:
More than 48% of applications now use Java 11 in production (up from 11.11% in 2020)
Java 8 is a close second, with 46.45% of applications in production
Java 17 has not climbed the charts, but is growing rapidly.
(For the record, Java 8 was retroactively designated an LTS release. Java 11 was LTS until the release of Java 17 last September. Java 18, the current version, came out in March.)
The class I taught this week involved newcomers to Java, though all the students did have a background in another object-oriented language, either Ruby or C#. Since they had no current investment in Java, they were willing to adopt the latest stable version.
One of the features added to Java recently (Java 16, to be precise) was records. A record is a class with a very simple syntax, which creates immutable objects with autogenerated toString
, equals
, and hashCode
methods. That means they’re perfect for converting from JavaScript Object Notation (JSON), as long as the JSON parsers understand that the record conventions are different and that the resulting instances can’t be modified.
The JSON parser that comes with the Spring framework is called Jackson, and it’s one of the few that works properly with records, at least mostly. One of the exercises we do in class is to access the Google Geocoder web service for a given address, which returns a JSON response that contains the latitude and longitude of that address.
Geocoder requests are of the form:
https://maps.googleapis.com/maps/api/geocode/outputFormat?parameters
where the outputFormat
is either JSON or XML, and the parameters are the URL encoded address and a key you have to register to get. The sample in the documentation is for Google Headquarters:
https://maps.googleapis.com/maps/api/geocode/json?place_id=ChIJeRpOeF67j4AR9ydy_PIzPuM&key=YOUR_API_KEY
This returns (assuming you supply a key):
{
"results": [
{
"address_components": [
{
"long_name": "1600",
"short_name": "1600",
"types": [
"street_number"
]
},
{
"long_name": "Amphitheatre Parkway",
"short_name": "Amphitheatre Pkwy",
"types": [
"route"
]
},
{
"long_name": "Mountain View",
"short_name": "Mountain View",
"types": [
"locality",
"political"
]
},
{
"long_name": "Santa Clara County",
"short_name": "Santa Clara County",
"types": [
"administrative_area_level_2",
"political"
]
},
{
"long_name": "California",
"short_name": "CA",
"types": [
"administrative_area_level_1",
"political"
]
},
{
"long_name": "United States",
"short_name": "US",
"types": [
"country",
"political"
]
},
{
"long_name": "94043",
"short_name": "94043",
"types": [
"postal_code"
]
}
],
"formatted_address": "1600 Amphitheatre Pkwy, Mountain View, CA 94043, USA",
"geometry": {
"location": {
"lat": 37.4224428,
"lng": -122.0842467
},
"location_type": "ROOFTOP",
"viewport": {
"northeast": {
"lat": 37.4239627802915,
"lng": -122.0829089197085
},
"southwest": {
"lat": 37.4212648197085,
"lng": -122.0856068802915
}
}
},
"place_id": "ChIJeRpOeF67j4AR9ydy_PIzPuM",
"plus_code": {
"compound_code": "CWC8+X8 Mountain View, CA",
"global_code": "849VCWC8+X8"
},
"types": [
"street_address"
]
}
],
"status": "OK"
}
In my regular exercises, we build a Spring app that uses simple Java POJOs (Plain Old Java Objects, i.e., regular classes) to model the Response
, the Result
, the Geometry
, and the Location
blocks to model the JSON objects. This week, however, I wanted to use Java records.
The resulting records are very simple, at least until I hit a problem.
Here is the Response
, where I added a couple of convenience methods:
import java.util.List;
public record Response(List<Result> results, String status) { public Location location() {
return results.get(0).getGeometry().location();
}
public String formattedAddress() {
return results.get(0).getFormattedAddress();
}
}
and here are Geometry
and Location
:
public record Geometry(Location location) { }
public record Location(double lat, double lng) { }
I wanted to make the Result
a record, too, which would be, simply:
public record Result(String formattedAddress, Geometry geometry) {}
This works just fine for the latitude and longitude, but fails for the formatted address. That’s because in the Java code, the Result
uses camel case for formattedAddress
, while the JSON data includes underscores instead for formatted_address
. To fix that, you can add an annotation to Result
:
public record Result(
@JsonProperty("formatted_address") String formattedAddress,
Geometry geometry) {}
That works, but it ties you to the JSON parser from Jackson, and you need to know that specific annotation. What we normally do in class is to go into to the application.yml
file in a Spring Boot application and add:
spring:
jackson:
property-naming-strategy: SNAKE_CASE
Yup, snake case. For those who might find such things amusing, here are the actual constants from the Jackson PropertyNamingStrategies class:
KEBAB_CASE, which I normally call train case. That’s lowercase letters separated by hyphens. Like train connectors, get it?
LOWER_CAMEL_CASE, like
formattedAddress
, which is what Java usesLOWER_CASE, which is all lowercase with no separator
LOWER_DOT_CASE, which puts dots between the words
UPPER_CAMEL_CASE, which would be
FormattedAddress
, used by languages like Pascal (wow, that takes me back, but that’s a story for another day)UPPER_SNAKE_CASE, which would be all uppercase with underscores, like the constants they’re showing. I always called that SCREAMING_SNAKE_CASE instead, mostly because I find it funny, and finally,
SNAKE_CASE, as in
formatted_address
, which is what the JSON data is using here.
If you set that property and use POJOs, everything works. If you do that with Java records, however, you get an error about not being able to modify the Result
object.
In class, we just converted the Result
record into a POJO, because that was easy enough to do. But it seems there are a few issues left in the world of Java JSON parsers.
An Excellent Take on Elon Musk
This week I read an excellent article by Charlie Warzel at The Atlantic on the subject of Elon Musk and his takeover of Twitter. It’s called Elon Musk Is Already Grinding Us Down, and is in a subscriber-only edition of Warzel’s newsletter, Galaxy Brain. Since many people don’t have a subscription, I thought I’d summarize some of its main points here. Honestly, though, I’d subscribe even if all I knew about that newsletter was this particular issue.
The basic idea, which isn’t all that original but is worth stating, is that Musk doesn’t really care that what he’s saying is wrong. He’s only trying to get attention.
This is a common activity among certain denizens of the web, including a former President. The spectacle is the goal as well as the reward. The idea is to say something outrageous, wait for the backlash, and then make fun of the people who got upset (“See? They’re going nuts over a meme! They’ve lost it!”). Then move on to the next topic, because they never really cared that much about the first one anyway. Those people who spend a lot of time and effort debunking the falsehoods only increase the attention that they wanted in the first place.
The newsletter includes this tweet, which states the problem so eloquently :
Every time Musk tweets something ridiculous, a lot of experts feel obligated to inform him how he’s wrong and try to get him to debate. But rational arguments aren’t the point — it’s all about engagement, and getting people mad increases engagement or Facebook wouldn’t be the trillion dollar company it is today.
As Warzel says,
Like Trump, Musk puts his critics in a real bind. Broadly speaking, in an attention economy (emphasis added) there’s no satisfying way to deal with people like them.
The attention gives them power over others, not to mention satisfying some deep dysfunctional need. They crave it, and will say anything to get it, regardless of its relationship to truth or reality.
The problem with Musk, of course, is that occasionally he accomplishes something. Warzel refers to him as a “bullsh*tter who delivers” (sorry about my own discomfort with profanity, but you get the idea). I would modify that to say Musk is a “bs-er who occasionally delivers,” because for every success he’s had tons of failures. Teslas are cool, and I still love mine. SpaceX is cool and gave us a real crewed spaceflight capacity and, by the way, lands booster rockets standing up on drone ships, which is still truly awesome. But there are also dozens of failures and broken promises along the way.
So what do we do? Back in the mid 1990s, the company where I worked finally got Usenet. Usenet was a text-based set of discussion groups that you could follow, a lot like modern Reddit, or even Slack or Discord. I followed a few groups (especially rec.arts.sf.tv.babylon5, where I treasured any posts made by J. Michael Straczynski, the show runner).
Anybody could make a Usenet group, which was especially easy in the rec.arts hierarchy, and if enough people voted for it, it became part of the standard feed sent around the world. While browsing through the topics one day, I encountered real, live Holocaust deniers for the first time in my life. At first I thought the whole thing was a joke, even in poor taste, but reading through the feed it rapidly became clear that the group leaders were serious, and were constantly posting about giving talks and meeting supporters and traveling around the world debunking the “myth” of the Holocaust.
Of course I was upset. But I noticed an interesting thing. There were a handful of posters that waited for each time one of the leaders added his bizarre, deranged take, and they replied. Their replies were simple, clear, and full of facts, with links to supporting documents, that refuted everything the original post said.
That sounds like bringing stats to a meme fight, but here’s the difference — the replies were not directed at the poster, they were directed at the readers. Every time I saw a crazy post and was tempted to get outraged, I’d scroll down and find the calm, clear, debunking evidence that was much more thorough and simple than I ever would have managed.
That, I think, is the biggest difference between then and now. The same loons are posting the same garbage, but the key is not to argue with them. That just gives them the attention they want and elevates them to the level of the experts debunking them. No, the idea was to talk to other readers, and give them the information they needed to see reality.
That seemed like a lot of work to me, and it may have been, though not as much as I originally thought. As you might imagine, people who deny reality tend to repeat themselves a lot. I expect some of the debunkers had documents with a set of arguments they had ready whenever they were needed, and then just picked one from column A and one from column B, and made the resulting posts.
Would that work today? I don’t know. I certainly don’t have the time or energy to do it myself. But from my point of view, the best part about those posts is that they kept me from getting too upset or even too invested. I was content to leave those original posts alone, knowing the debunking follow-up was available if I needed it, which I rarely did. That way I could just leave that group and never return.
We have all kinds of fears surrounding what Musk will do with Twitter, and his tweets since he acquired the company have only made those fears worse. But the one lesson I keep in mind is not to try to argue with him. If I post at all in reaction to one of his idiotic tweets, it’ll be here, and for you. After all, you’re the people I actually care about. :)
My Chess Weekend
As long-time jarheads know, every once in a while I enter a chess tournament. I keep working at my game, at a very low level given my current time constraints, but I enjoy the game and keep thinking I can raise my rating if I only work hard enough. This weekend, in my home state of Connecticut, a one-day tournament was held called the 2022 Connecticut Senior Championship. Since one of the problems I have with resuming my tournament career is I get very tired of getting crushed by 12-year-olds, I decided to enter.
The event was scheduled for yesterday (Saturday, April 30). As of Monday last week, I was the only person entered. That changed that afternoon:
By Thursday, there were still only four entries. I sent an email to the organizer asking if the event would be cancelled if they didn’t get enough people, and he assured me it would run anyway.
Registration ended on Friday, and by then we were up to nine players, including, I kid you not, Grandmaster Sergey Kudrin (age 62), rated 2469:
Yikes. As you can see, there were ultimately four of us at the bottom of the list, rated between 1400 and 1503. Then there was a jump to 1856, and then four players at the top rated 2020 and up.
To give you a sense of what that means, a 200 point gap in rating means in a match of ten games, the higher rated player ought to win six or seven. A gap of 400 points means the higher rated player ought to win 9 out of 10. In other words, I was a big underdog against anyone outside that bottom group of four.
Here’s how it went:
You can read the whole series on Twitter if you like. Suffice it to say my opponents were rated 2221 (I lost), 1448 (I won), 1503 (I lost on time in a better position, grr), and 1856 (I lost, but I made him work for it). It’s a good thing I won that one game, or it would have been ugly.
I talked to my wife when I got home.
I met GM Kudrin very briefly. He wore a mask the whole time (masks were optional for vaccinated players), but he was clearly reserved and didn’t want to hang out with anybody. I was going to ask him for a picture, but decided he’s be happier if I left him alone. I’m pretty sure he won every game, but the official results haven’t been posted yet.
It was a pretty good experience, I guess. As my wife said, it got me out of the house and around actual people for a while, so that’s good. Best of all, I didn’t get clobbered by anyone who still had a parent-mandated bedtime, so that definitely goes in the win column.
Other Tweets
This is a joke based on Star Trek II, III, and IV, so pretty obscure, but I laughed:
(I extracted the image from the tweet to make sure everybody could read it without going to Twitter.)
I thought this one was good too:
I had to think about this one for a minute, but it was worth it:
I have to include a Dad Joke:
And finally, there’s this:
For those whippersnappers too young to remember, Susanna Hoffs was the lead singer in The Bangles, whose best song was Manic Monday:
Have a great week, even if it does start on Monday.
As a reminder, you can see all my upcoming training courses on the O’Reilly Learning Platform here and all the upcoming NFJS Virtual Workshops here.
Last week:
Private class on Java and Spring for Ruby on Rails and C# developers.
This week:
Private class on Java and Spring for Ruby on Rails and C# developers.