Java 14, CSS Popovers, Pandemic, Self Affirmation

CodeFX Occasionally #77 — 19th of January 2020

Nicolai Parlog
Jan 21 · 16 min read

Hi everyone,

I’ve spent a few hours this year digging into Java 14 and because I’m not going to have the time to write proper posts about that any time soon, I want to at least share my notes. The lack of time stems from working on my new site: It’s a lot of fun and coming along pretty well and I thought I might tell you a little about my adventures in web development, so I summarized how I created an animated, pure-CSS popover. Then: “Why fixing world crisis is fun” and finally a few words on why I’ll be bragging (even) more on Twitter now.

I send this newsletter out some Sundays. Or other days. Sometimes not for weeks. But as an actual email. So, subscribe!


Java 14

I’ve spent two streams exploring Java 14 (if you want to see the videos, Twitch will keep them around for about two more weeks). The first was dedicated to records (I tweeted about this portion and include the replies below), the second to everything else. Here’s a brief rundown.

Records

JEP 359 introduces records to Java:

Records are a new kind of type declaration in the Java language. Like an , a is a restricted form of class. It declares its representation, and commits to an API that matches that representation. Records give up a freedom that classes usually enjoy: the ability to decouple API from representation. In return, records gain a significant degree of concision.

First and most importantly, I really like the concise syntax for records:

record Point(int x, int y) { }

For that you get final fields and , a 2-arg constructor assigning them, "getters", , , . The "getters", accessors really, don't have the "get" prefix and instead have the same name as the fields: and . I avoid for years now (and updated my IntelliJ templates accordingly), so I like that decision:

point.x()

Looks great!

You can customize records by implementing interfaces (it’s not possible to extend classes), overriding provided methods, and adding new ones (but no additional fields). You can also add annotations to components (that’s what and are called in ). Records are implicitly final, so they can't be extended. You can also customize the provided constructor (called the canonical one)...

public Point {
// do something with `x` and `y`;
// to my surprise, fields are assigned
// after this block was executed
}

… and provide new ones.

public Point() {
this(0, 0);
}

It’s good to have those customization tools, but I think 90% of records won’t need them.

Our experiment unearthed two inconsistencies in the JEP, which Tagir Valeev already reported. I also thought it was weird that you can add to a field even though its accessor doesn't implement a method from an interface.

During our exploration we discussed a few questions. I’ll include the answers I got on Twitter, but I’m also interested to hear your opinion on them, so let me know what you think!

“Why isn’t this called a struct?”

I guess the name has too much baggage that doesn’t quite fit record’s programming model in Java?

Oracle’s Brian Goetz, in charge of extending Java with records, replied:

Because they’re full classes, with methods, state, identity, etc.

“Why accessors instead of public final fields?”

Another one I can’t quite answer. I guess field access from outside of a class is still very odd and using methods is just “more Java”?

Turns out, there are quite a number of pretty good reasons:

Gunnar Morling:

I could see some reasons for that, e.g. that way you can add additional derived properties, and all props (“real” and “virtual” ones) are accessed uniformely.

Tagir Valeev on why you would want to override an accessor (which is of course not possible with direct field access):

* You can return a copy of mutable component.
* You can wrap a mutable component into unmodifiable view.
* You can log accesses, count access stats, etc.

Brian Goetz:

Among other reasons, combining records with mutable component types (E.g. arrays) becomes unworkable. And, because doing so would make every client have to think about whether a given type is a record or a class. And because migration. And….

“Why not reuse record instances with the same value?”

This one I can answer: Because it’s hard. This is essentially a cache (whether at run time or at compile time) and it’s highly questionable whether operating one is worth it. Also, locking on shared instances anyone?

On stream as well as on Twitter, the and caches were brought up. While superficially similar, I don't think they're a good reference. Those are JDK classes that the JVM can make a lot of assumptions about. The same would not generally be true for user-defined records.

Tagir Valeev went into more detail:

You can implement such kind of cache on application level, and it would be much more flexible. E.g. you can use weak refs, or cache within single request/subtask, them drop it; you can use thread safe or not map depending on your needs, you can cache with some probability. You can add custom expiration rules, limit cache size, etc. I guess Guava already does most of this. Really no need to put this complexity into a language of library can manage it.

“Why can’t records extend other records?”

A record makes the promise of exposing its entire state via accessors. This would not be true for records that are being extended. I get the argument but would that be so bad? On the other hand, they can implement interfaces, which goes a long way.

Once again, Brian Gietz chimed in:

It’s possible, some day, we will have abstract records. However, in our exploration, even this is more complicated than it looks, so it is still on the “possible ideas for the future list.”

“What about backward compatibility?”

I didn’t try this, but the bytecode looks like turning a properly written class into a record and vice versa should be binary compatible. That’s pretty neat! Even if you’re not on Java 14, you can already lay the groundwork for records.

“Are records serializable?”

Not by default although they are a good fit for being turned into bytes — after all records are just data. 🙂

And that shows by how serialization can be implemented for them — much less magic than for regular classes. From the preliminary Javadoc (thanks Chris Hegarty for pointing this out):

Records are serialized differently than ordinary serializable or externalizable objects. The serialized form of a record object is a sequence of values derived from the record components. The stream format of a record object is the same as that of an ordinary object in the stream. During deserialization, if the local class equivalent of the specified stream class descriptor is a record class, then first the stream fields are read and reconstructed to serve as the record’s component values; and second, a record object is created by invoking the record’s canonical constructor with the component values as arguments (or the default value for component’s type if a component value is absent from the stream).

So far, so good, but there’s a surprise hidden in there (when isn’t it?):

Like other serializable or externalizable objects, record objects can function as the target of back references appearing subsequently in the serialization stream. However, a cycle in the graph where the record object is referred to, either directly or transitively, by one of its components, is not preserved. The record components are deserialized prior to the invocation of the record constructor, hence this limitation (see [Section 1.14, “Circular References” for additional information).

Oops. That immediately made me wonder how it can break an application. Not too hard, actually, think about a possibly cyclic graph:

record GraphNode<T>(
T data,
List<GraphNode<T>> edges) { }

Of course, needs to be a mutable list to create a cycle, but that's not a far stretch. I thought this was pretty clever, but as Tagir Valeev rightfully pointed out, this would break 's default implementation. It calls on all fields and with a cycle in the graph it would eventually go, well, in cycles. ~> .

I initially missed that and one would have to overwrite to prevent it, but that seems to violate the "the whole state" idea. That said, what's a simple node but the information it contains and its edges? And isn't that what records are about? Having a simple and semantically meaningful way to declare simple aggregates? Feels like in is a leaking implementation detail.

That’s all on records — here are my brief notes on the other features.

Pattern matching for instanceof (preview)

Introduced by JEP 305 — works like this:

Object obj = // ...
if (obj instanceof String s) {
// can use `s` as `String` here
} else {
// can't use `s` here
}
  • this is always a two step process: (1) predicate & (2) extraction
  • the extracted variable is in scope within the rest of the condition and the true branch
  • this should remove many explicit casts
  • it is particularly interesting in
  • also works in the (ternary) Boolean expression and for method returns

Packaging tool (incubator)

Introduced by JEP 343 — looks like this:

$ jpackage --name myapp --input lib --main-jar main.jar

[This] will package the application in the local system’s default format

From earlier in the JEP:

The application may be provided as a collection of ordinary JAR files or as a collection of modules. The supported platform-specific package formats are:

* Linux: deb and rpm
* macOS: pkg and dmg
* Windows: msi and exe

My notes:

  • why (but for modules?!)
  • three-step process:
    1. create runtime image with jlink
    2. create application image
    3. package application image
  • jpackage can do any and all of those
  • why not cross-OS?
  • doesn’t work at all on my Gentoo Linux (claims and are unknown formats)

Helpful NPEs

Introduced by JEP 358 — looks like this:

Exception in thread “main” java.lang.NullPointerException: Cannot assign field “name” because “user” is null at Prog.main(User.java:15)

My notes:

  • looks very helpful
  • activate with
  • be aware of variable name leaks into logs / prevent by compiling without debug info (no )
  • can’t show array indices
  • would be nice if could generate a similar message

Switch standardized

JEP 361 standardized expressions with no changes since Java 13.

Text blocks slightly enhanced

JEP 368 extended the text block preview because it added a new detail:

  • ending a line with now prevents a line break
  • inserts a space, which is particularly useful to keep trailing white space from being trimmed

Everything else

Here’s the list of the other JEPs:

  • JEP 345 — NUMA-Aware Memory Allocation for G1
  • JEP 349 — JFR Event Streaming
  • JEP 352 — Non-Volatile Mapped Byte Buffers
  • JEP 362 — Deprecate the Solaris and SPARC Ports
  • JEP 363 — Remove the Concurrent Mark Sweep (CMS) Garbage Collector
  • JEP 364 — ZGC on macOS
  • JEP 365 — ZGC on Windows
  • JEP 366 — Deprecate the ParallelScavenge + SerialOld GC Combination
  • JEP 367 — Remove the Pack200 Tools and API
  • JEP 370 — Foreign-Memory Access API (Incubator)

On February 13th I will likely have Oracle’s Jorn Vernee on stream to tell us about the foreign-memory access API that he helped implement. Don’t worry if you don’t know much about Project Panama or native programming in Java — neither do I, so he will have to explain a lot. 😊


nipafx.dev diary — CSS popover

My new site is slowly coming together and I recently generated all the redirects from the old site to the new. Thing is, it’s a different domain with a very different look and I’m a little afraid that some regular readers may be confused and leave. I’d also like to give at least a very short motivation for the change.

Easy, just encode the fact that the current reader was forwarded in the URL and show a popover that explains what’s going on — preferably without JavaScript. So off I went!

First iteration — display: none/block

I’ve recently learned about the CSS pseudo class and thought it could be of value here. It selects an element if that element's ID matches the current URL fragment. So:

  • forward readers from to
  • use an anchor (resets the URL fragment) as close button
  • give the popover the ID , hide it by default but show it with

That’s easy anough:

#codefx-forward {
display: none;
}
#codefx-forward:target {
display: block
}

Nice, done.

Then again, do I really want to show this every time a user comes from codefx.org? No, they should have the chance to accept a cookie and never get that popover again. That requires JavaScript (fair enough), but that’s no reason not to keep the non-JS variant functional.

Second iteration — remove fragment with JS

So I put the “Got it” button in a div and added a second div with two close buttons, one accepts a cookie, the other doesn’t. The second div is hidden by default but if JS is active it displays it while hiding the first div. As a result, without JS, the pure CSS variant works as before, while with JS you get richer options.

All that’s left is to set a cookie (first time I wrote code for that!) for one of the buttons and, on page load, to look for the cookie (first time I write code for that — boy, what a sucky API) and change the URL fragment if it is found, so the popover remains hidden.

Nice, done.

But… look closer, yeah, on some page loads with cookie the popover briefly flashes before the JS code removes the fragment. Ugh. Ok, I need to delay the popover appearance long enough for the JS code to kick in. I already thought that a popover that fades in after a sec is much nicer than one that’s just there, so let’s do that.

Third iteration — transition opacity

Quick change:

#codefx-forward {
display: none;
opacity: 0;
transition: opacity 0.5s,
}
#codefx-forward:target {
display: block
opacity: 1;
transition: opacity 0.5s 1s;
}

Nice, don… what? Why doesn’t this work? It never fades — not in, not out, what’s going on here?

Looking closer, of course it doesn’t fade out. It goes from to , so by the time opacity is transitioned, it's already invisible. But why doesn't it fade in?

After quite some time I found someone (on StackOverflow, I think — can’t find it ritght now) claiming that if changes to something visible, there aren't any transitions and everything appears in the end state. No idea whether this is generally true (nobody contested it) and why that would be the case, but it surely matched my experience.

Damn, what now?

Fourth iteration — translate

After beating my had against the Google wall for a while longer I eventually picked up an alternative way to hide something from view: . (And as I'm writing this, I wonder, wouldn't also work? I've used it elsewhere, so why didn't that occur to me. Damn you, brain!) While old example often translate by a large number of pixels, I went for the way hipper variant of using view port units:

#codefx-forward {
transform: translateY(-100vh);
opacity: 0;
transition: opacity 0.5s, transform 0s 0.5s;
}
#codefx-forward:target {
transform: translateY(0);
opacity: 1;
transition: opacity 0.5s 1s;
}

Now there’s a 1 second delay before the popover fades in (plenty time to change the URL if a cookie is present) and, thanks to the delay on the transition, it even fades out.

Nice, done.

(To be continued?)

A screenshot of the beautiful popover

Pandemic

Anyone interested in a game of real-life Pandemic? Looks like the first three cubes just went down in Bejing:

China Reports New Cases of Deadly Virus, Adding to Fears of Outbreak

The spread of the pneumonialike illness comes ahead of the Lunar New Year, when hundreds of millions will travel.

Wait, let’s track back a little. I recently got the board game Pandemic. It’s 12 years old by now and has been in my list for a while, but we were so busy with Terraforming Mars that we never tried it — big mistake, it’s so cool!

It’s a cooperative game where the group tries to prevent the world from succumbing to deadly diseases. It’s comparatively straightforward:

  • the board is a world map with 48 cities, divided into four regions/colors
  • there are four diseases, each symbolized by 24 cubes that get placed on the cities as diseases spread
  • a stack of infection cards (one card per city) governs where diseases spread
  • a stack of player cards (also one card per city plus a few special events) governs what the players can do
  • each player has a pawn that can move around the map and treat diseases (remove cubes), build research centers, share cards, and discover cures
  • each player has one of five or more roles (e.g. medic or dispatcher) that are better at certain actions (e.g. treatment) or can do all-new things (e.g. moving other players’ pawns)

The players win when all four desease cures are discovered. The players loose when there are no more player cards, no more cubes of a color, or too many outbreaks.

A player’s turn looks as follows:

  • be overwhelmed by the horrible state the world is in right now
  • discuss with your team mates what you could possibly do to help
  • have a look at their cards and their options
  • try to plan seven turns ahead
  • see the futility of it all and settle for just the next two turns
  • tentatively consider an action or two
  • no, think again
  • eventually settle on four actions (like moving, treating, building, etc.) and execute them
  • discuss whether those were really the best options you had
  • draw two new player cards (cross fingers that there’s no epidemic card among them)
  • draw two new infection cards (and cross fingers it’s not the ones you really don’t need right now) and place disease cubes accordingly

The epidemic cards (drawn with player cards) and outbreaks (happens when a city would get its fourth cube of a disease) add “a fun element of surprise” (read: a horrible specter of impending doom) to the game and, together with its many failure modes, make it quite challenging — you need to be reasonably good to win about half your games on normal difficulty. Together with some clever yet simple rule twists all this makes it very entertaining and replayable.

But that I like the game isn’t the reason why I bring it up. It’s how well it performs outside of my very small circle of board gaming friends.

Exhibit A, the lady I live with: She really doesn’t like card or board games and one of the very few times I convinced here to give it a shot (we were traveling in Bolivia and I was stuck in the hotel room because of mild food poisoning — she had pity and wanted to humor me), ended with her throwing the Settler cards across the room, because she felt like I was pulling ahead. 10 minutes in. Threw the cards. Across the room. A few days ago, we played Pandemic three times on one evening.

Exhibit B, the much smaller lady I life with: She’s six, just started reading, and has trouble finding Europe on a globe. Yet she was so curious what that game was that even mum played that we gave it a shot. And with some adjustments (decreasing difficulty and placing additional markers on the board to indicate which player cards we hold) we played two games in the recent days with her holding her own and really contributing to the progress.

Exhibit C, me: When I was recently starved to play Pandemic, I considered buying the PC version to play solo. It eventually dawned on me that there’s no need because the board game is fully solo-able. Look at the rules above — you can play as any number of players without considerably changing the game (you just loose the communication aspect, but who needs people, right?). So I spent an evening playing board games by myself. 🤓

There are a few reasons why I think it performs so well with non-geeks:

  • saving the world from diseases is
    1. a very agreeable motivation and
    2. not a far stretch for those who don’t regularly hang out in Middle Earth, the Koprulu sector, or dank dungeons
  • as many people, both ladies I live with are inherently interested in traveling, in other customs, in other countries — having a world map as a board works really well with that
  • while the game can be complex, its rules aren’t (they’re quickly explained and easy to grasp) and the game state is quickly assessed
  • the difficulty is very easy to scale: just vary the number of epidemic cards from 3 (pretty easy, even for kids) to 6 (nightmare)
  • it’s collaborative and equal parts puzzling and communicating — who goes where to do what, which cards do you have, how can we get you in the right place for your special power

Even though they are very different games, there’s one thing that it has in common with Terraforming Mars: It’s played on a map. No wait, it’s that whenever I finish a game, I’m immediately curious about how the next one goes. (Ok, two things then.)

If you like board games, get Pandemic. If you don’t like board games, but have looked at a map this passing week and can generally sympathize with not letting the world cough out its life, get Pandemic.


Working on myself — self affirmation

If you’ve read my newsletter, particularly those issues that cover the turn of each year, you know that I often struggle with the feeling of not getting enough. Here’s a little more background on that and one small thing that I want to do in 2020 that hopefully helps (once again lifted from a twitter thread):

At the beginning of each year, I make goals for my public self: “write x articles”, “finish project y”, … stuff like that. I’m not the most disciplined person, though, and after invariably distracting myself with something shiny, I fall behind. (Or at least I think I do.) I start shit-talking myself, being angry for being undisciplined, getting stressed out for not achieving what I set out to do. I get unfocused and fussy, eventually turning what could have been a slight dip in productivity into a steep drop.

Even after pulling myself out of it again, the foul taste of being behind, of already having lost the year, never quite goes away. Then comes the last week of December, the time to reckon…

… and it always turns out it wasn’t as bad as I thought. Quite the opposite, the actual data looks pretty good. Visitor numbers, income, new projects off the ground, etc. Why did I feel shitty again? Then, a few days later, the next year starts and the cycle begins again.

So for 2020 I want to try something new. I want to visibly track my achievements, so they don’t get lost in the hustle. A (hopefully long) thread of published posts, given presentations, Twitch streams, and YouTube videos.

And I started right there, tweet-quoting my first four achievements of the year. It may seem very self-congratulatory… and it is! That’s the whole point! To congratulate myself on getting something done, so maybe, when I invariable start thinking that I’m a lazy idiot who never achieves anything, it doesn’t get quite as dark and demoralizing.

Let’s see how it goes.

so long … Nicolai


PS: Don’t forget to subscribe or recommend! :)

CodeFX Weekly

Whatever caught my interest throughout the week: libraries or tools I am using, what I’ve written or read that might interest you, fascinating questions on StackOverflow, conferences visits, odd tweets, anything really that connects to Java, software development, or writing.

Nicolai Parlog

Written by

Developer (mostly Java), blogger (http://codefx.org/), author (http://tiny.cc/jms), trainer (http://courses.codefx.org & https://www.youtube.com/c/codefx)

CodeFX Weekly

Whatever caught my interest throughout the week: libraries or tools I am using, what I’ve written or read that might interest you, fascinating questions on StackOverflow, conferences visits, odd tweets, anything really that connects to Java, software development, or writing.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade