jPrime 2026

Never a Null Moment with JSpecify, Hinse ter Schuur

50:53 · 03 Jun 2026 – 04 Jun 2026 · YouTube

About this talk

This talk explores the JSpecify library, a Java annotation library aimed at improving nullability management in Java applications. The speaker, Hinse Ter Schuur, shares personal experiences with null pointer exceptions and how they can disrupt application performance, particularly in the context of a sports analytics app he developed. He discusses the principles behind JSpecify, emphasizing its role in making nullability explicit and providing compile-time safety without introducing new syntax. The speaker elaborates on how JSpecify can be gradually integrated into existing codebases and discusses the limitations and challenges, particularly regarding runtime issues like null pollution in libraries such as Jackson. Overall, the talk highlights how JSpecify can aid developers in avoiding null-related errors while building robust Java applications.

Full transcript

Welcome everyone. Still a few people came in even on a a nice sunny afternoon. And welcome to the session never a dull moment with J specify. Who here does some side projects like in Java? I see some hands 1 2 3 4 couple of people. Who actually bring their side projects to production kind of not to make money but to have people use it? Oh, that's a

bit less. One more hand there. So I think it can be pretty useful to bring your side projects to a production like thing. You will probably learn a lot from it. I can tell from experience. That's what I want to say. Um So one of my side project has to do with korfball. Which is really a Dutch sport. So you might not know korfball but it's one

of my passions and the other passions is photography. So I took this picture last Saturday. Not with the screens there of course but when it comes to the side project that's what the apps are what you're looking at. So I build an app that can where you can tag your events in a sports any sports match basically but I I focused it on on korfball this sport.

And it can also get some statistics out of it. So I build that for my wife who's also a trainer so she practiced and used that and then the the trainer of the club of the the first team the main the main team asked hey can I use your project? It seems very nice so you have statistics and all. Can I use So of course so I

gave them access to the app and um a friend of mine was doing the tagging live during during the matches and at one match I was watching the game unfold and she was tagging like putting where the shots were taken and noting things down in the app and all of a sudden she stood up from her chair, walked away, walked to her back and took out a

notebook and a pen because the app had an issue. Turned out later there was a null pointer exception so there was really a null moment where you don't want it like in the middle of the game. She managed to write some things down on a notebook but it's different especially if you hope to have the nice data from the app. So that was really like an experience

with a null moment. And I'm pretty sure that you also have some null moments at some point in time, right? Who never had seen a null pointer exception? Never? Ah, okay. Exactly what I thought or otherwise you might not be programming in Java, right? Okay. >> [snorts] >> So a bit about me. So I put it down in some JSON format which is actually quite old-fashioned. So

I should maybe put it like this like it came out of an LLM. So my name is Hinse Ter Schuur. I'm from the Netherlands as you might tell from my accent maybe. I work for a consultancy company called SDB and I'm based in The Hague. I've worked for roughly 20 years with Java, did also a bit in Scala where I didn't have any null pointer exceptions to

be honest. Just one. Um, but my main focus is is back end and I worked for a couple of clients in different industries like logistics, finance and also some government projects. Let's talk about null. Tony Hoare said uh, a while ago, 2009 which is already a long time ago. He called it uh, his billion-dollar mistake, the invention of the null reference. So, you probably heard about this,

um, this quote. And the JSpecify, if you go, okay, let me first say, JSpecify is a library, a Java library that tries to, uh, help with, uh, a nullability. So, it tries to make it explicit, as we'll see later. Uh, and if you look at the JSpecify website, you see that they also recognize this maybe billion-dollar mistake, or not so much. So, what the JSpecify design FAQ

says, "Null itself is not the billion-dollar mistake." The mistake is when a language automatically makes null uh, a member of every reference type. I think that's that's basically the problem, and that's what JSpecify tries to correct in a certain way for Java. So, what is JSpecify? As I mentioned, it's an annotation library. Uh, you can find the website at jspecify.dev. And it's a mainly a specification and

a library, uh, and it's backed by some large industry leaders, like Google, JetBrains, and you can see them all here. So, some interesting ones, JetBrains, who builds, uh, Kotlin, and, uh, builds IntelliJ. Uh, we have Oracle, who's responsible for Java, right? We see Broadcom with, uh, the introduction, or with Spring. Uh, and actually, Spring was the thing that, uh, made me aware of JSpecify. When I run

into that problem with the null pointers, then a couple of months later, I I found out that, um, Spring was actually, uh, going wild on, uh, on JSpecify. So, I thought, uh, let's take a look, and and yet, here we are. Um, but the J Specify project, it took actually some time uh to get there. It took them I think 2 years if you can follow the

blog post. So, they put up a blog post in 2022 and they released version one of J Specify in 2024. So, that's 2 years. Uh and you would expect like uh really large body of work. But, if you look at the code, it's very minimalistic. So, there are six files. Four of them are annotations. 51 lines of code. Most of it it's uh package and import declarations

and some uh some annotations. But, most of the time went into specifying what it actually means null or non-null. Uh and how all the semantics work if you use those annotations in your project. So, if you look at the website, there's a lot of documentation and a lot of background around why they choose those specific semantics. And they also explain the semantics uh very well. Uh basically,

J Specify, it's all about the types. It's not so much about the value itself. It's as I mentioned, it's the type that does or does not include null. That's the main uh question they try to answer. Is null intended to be included in this uh in the value set of this type? So, standard in Java, every reference type includes null. So, if you have a string, it

can be foo, bar, or baz. It can also be null. You don't know. There's no hint that it that it should could contain null. And what J Specify does, it introduces nullable types uh and those basically don't include null. So, then for a string, if it's a uh sorry, a non-null type, then it cannot include null. I think there's a typo here on the slide. It says

nullable type, which actually no. Okay. Um but if you think in types, then you can also think about Okay, a non-null string is kind of like a subclass of nullable string. So, the the nullable string can basically be anything, including null. And non-null is a specialization which does not include uh null. So, the basic principles when working with JSpecify, um you want to explicitly opt in if

you want to use null for a type. They didn't want to introduce uh new syntax because that's uh hard to get the adoption right. So, they just have some annotations. They're aiming for compile-time safety. And that's important so that you can uh get your feedback quite quickly. When you compile your project, you can immediately see whether everything is working correctly with regards to uh nulls. And also

what uh what is important uh is that there's no implementation of the checking. So, it's only the semantics of the annotation, and they explain what it means, and they explain what a checker should do, but they didn't build a checker tool. So, that's for uh vendors of of tools who provide uh vendors of checker tools, basically. So, that could could be IDEs or Maven plugins or Gradle

plugins or whatever. But basically, it's up to you. That's also an interesting uh point that you can uh use your own checker tool. So, for example, Spring Boot uses uh JSpecify annotations, but they don't enforce the use of any checker tool. And that's also nice thing. You can pick the checker tool you you like, and they use whatever tool they like. But the the only contract that

they have is like the nullability specification or the JSpecify specification. So, how do you exclude null from a type? You mark it as nonnull. Obviously, sounds very reasonable. And if you want to explicitly say, "Okay, this type does include null." then you can add the nullable annotation So, if you see that in action in a simple example, is it readable in the back, by the way? People

in the back row, they're asleep. No, they're awake. Good. Uh awesome. Uh so, in this case, uh if we're looking at an at a match, uh what we're looking at here, so it's uh the constructor for a for a game, basically. So, there's a home team and away team. Those cannot really be null. So, uh this is nothing new, right? And you can also say, "Ah, the

venue is nullable, so we don't know whether there's a location already. Maybe they figure it out later where the uh match will take place." It's a very simple example. Um but if we think about uh because we saw here the nonnull, nonnull, and a lot of nonnulls. And in practice, I think a lot of us want to use nonnull by default. We're not that much interested in

using null values. Sometimes they're they're pretty useful, uh but you want to have them uh nonnull by default. So, exclude null by default. And you can do that with J Specify by putting a null marked annotation on either a module, a package, a class, or even a method or constructor. And null marked basically means uh there's no null unless I explicitly mark it as nullable. So, if

you put that on your package, everything in that package is considered to be uh nonnull by default. Um and if needed, you can put the null unmarked annotation somewhere deeper. So, suppose you annotated a package with null marked, so you're saying everything is uh non-null, then with the null unmarked, you can say, "Okay, this piece of code, this method, or this class uh we didn't figure out

the nullness yet, so we put it on null unmarked, so that's basically the Java default. So, Java default doesn't say anything about whether something can be null. So, it's kind of an escape hatch out of the uh null marked context. And everything outside the null marked code is considered unspecified. And unspecified basically says the checker tool can do whatever they like. It's not like prescribed by J

Specify how you should interpret that, whether you should interpret it uh pessimistic or optimistic, they they don't specify that. The nice thing with having these null marked and null unmarked annotations is that you can gradually introduce J Specify into your code base. So, you don't need to annotate everything all at once to make it work. You can start with just a single class or just a single

package where you uh think it it's a best fit. Uh maybe also interesting to note that uh some of the well-known uh and and nullability annotations are not really type use. They're like uh specifying that a value can be null, like a specific um uh variable or field. But this is really saying something about the type. So, you could also see if you have a list, you

can even say that it's um uh a list containing non-null. So, this is in a null marked context a bit strange, but you can also say I can have null elements. So, the list itself can either be the list can be null or not. And you can also say something about the elements within those lists, and that's what you can see here in the second uh second

bit. Um if we see that in action, let's see. Let's take the the right one. Is this also readable? Yep, good. Um so here we have the non-null nullable annotations. Uh this is an event that that's something from my app. This is the simplified version, of course. Um and if we look at uh tests, we can see if we scroll down. So a player can be null,

there's no complaint here. But for example, a match, if we look at the match class, match is non-null. So if suppose I try to add a null here, IntelliJ should complain about this. It does. Uh it says, "No argument to parameter annotated as not null." That's probably not what you want. And we can also see it in the problems view. So that's all what we expect. And

then I mentioned we can uh use null marked to have the because we see non-null non-null non-null here. So we So we see quite a lot of non-nulls. So we can mark this as null marked. Where we basically say, "Okay, everything here is non-null unless specified otherwise." So now we can remove this one. This non-null because it's um obsolete. It's implied by the null marked. Also for

this one. And also for this one. And still we have the checks here that if we pass the null here, it will it will complain. all fine. So this is already very useful. But here I only marked this event class as null marked. I didn't say anything about the test. So, we could also do the same with the test. We can mark that one also as no

marked. We also want to have uh like the non null by default here as And then we already see some problems appearing. Uh so, for example, we see here it says something like, "Hey, you have a player that could be null and now you're passing it to a function and this is supposed to be non null, right? Because we're in the null marked context. So, this is

not allowed to be So, we can quickly fix this by this case we could There are several options, right? We could do check here or we can make this nullable. then um yeah, everything should be good, right? Uh looks perfect. But, if we look at the uh problems here, we see that there's still a problem. And often how it goes with null pointer exception. You have some

assumptions about your code. Oh, this is never be But, if we look at this, apparently we have a constant which you all expected it to be something like non null. But, apparently in some cases it could be null or at least in theory it could be null. And of course, this is a bit far-fetched, right? It was only to to trick you kind of. But, now it's

complaining, "Hey, you're uh having a a field here which is supposed to be non null. You assign it to null value." So, now you're also forced to look into uh look into this. Um so, this is really like the basics of the the J Specify things. Um we can get back to the slides. Um so, what as I mentioned, what triggered me to look into J Specify

was the adoption of J Specify by Spring Boot. Actually, in Spring Boot 4, they adopted J Specify widely. So, basically every of their module now has the J specify null marked and the nullable annotations where applicable. So, if you use stuff, also if you use the REST client and you get the body from from a REST call, it says it could be null, be aware. So, that's

that's very interesting. But, there's also adoption in JUnit, Guava, and all all those libraries. Maybe interesting to mention is that Kotlin also recognizes these annotations. So, then it interprets those as being like Kotlin standard types with the with the question mark if something is nullable. Um so, it it it uses the explicit nullability and the the non-null by default. That's That's very nice. how do you do

this in practice? Because I I showed how you can put your annotations there, but as I mentioned, just having the annotations doesn't do that much. Although, in IntelliJ, it's already doing a great job because of the the checks you have in IntelliJ, you get them by default. Uh but, someone implemented those, right? So, that's uh also something you probably want to have in CI because who's always

looking at the problems view in IntelliJ before they commit? there's one guy. Oh, couple more. But, I think the majority says, "Okay, there are some problems." Or maybe you don't even open up the the problems view, right? So, even now I was really looking at it, but in practice, you kind ignore it unless you're looking for something specific. So, it's good to have something in your build

pipeline or in your Maven or Gradle process that that also checks these things. So, it's good to check a tool that can check those nullability annotations. yeah, and that will do it for you in CI, which we'll see a bit later. Not the CI part, but the Maven checker tooling. And suppose you already have some existing existing nullability annotations like JSR JSR 305 or or some JetBrains

annotations, there's a nice tool called OpenRewrite which can do a lot of automatic rewrites, deterministic, so there's no AI involved in rewriting your but it can rewrite a lot of those nullability annotations into new JSpecify annotations also with the right semantics. But I don't I don't think it does a full rewrite. There are some situations where it can't handle, but in general it does a pretty good

job in converting a lot of these annotations So, when you want to get started, you start with adding null marked to a single package or a single class. Then you look at the warnings. That's the moment when you look at the those warnings actually. And then you fix them either by adding maybe some nullable annotation, or if you're really sure that it never happens go this should

never happen. Like it was in the opening session, there was also a statement about this They said, "Okay, put a system exit in there." That was a nice nice thing. I don't think you should do that maybe, but a suppress warnings can always help. Or you can if there's a large piece of code, so you mark your package as null marked and then you come across a

class that you cannot really work with it right now. You can null unmark it and basically everything works as before. And then repeat that process. So, for checker tooling, there are some existing ones as I mentioned, the the checks in IntelliJ ID idea are very good. So, they they get a lot of warnings. You can also upgrade them to errors if you want to. And for your

for your build like Maven or Gradle, you can select from a bunch of tools that claim to support J Specify to some extent. Because some of these tools support partial partially the the J Specify annotation, but for example, not on all the cases with generics. Uh so, that's a bit of a bummer. So, I think there's still some more work to be done, but I think it

helps that J that Spring already adopts J Specify that will probably push the checker tool vendors to to improve on that. There's also a reference implementation provided by J Specify, but they explicitly mentioned it that it's not production-ready and it's a bit could be a bit clunky to work with. Uh so, maybe you could look into it. But for my examples, I'll be working with NullAway. And

NullAway was an already existing checker library. It's built by Uber. And it's based on ErrorProne. So, it's kind of like a stack of tools. So, ErrorProne is a kind of a compiler that that's used to find bug patterns at compile time. And they provided this as a plugin to ErrorProne. Having it as a plugin for ErrorProne is nice, but it also comes with some configuration. So, if

you look at your POM file, for example, it can be a bit noisy. There's quite some stuff you need to configure NullAway. So, this is not the hard part, right? We know how to read the group and artifact IDs. But then we get to the compiler arguments, and then it gets interesting. Uh so, what I did, I just copied the settings from Spring Boot kind of, and

then it worked and tweaked it a little So, um NullAway on itself, even without the existence of J could already do some null checks, but now you could also put it in J Specify mode and then it tries to follow semantics as closely as possible. And you can instruct it to only check code that is marked with You can also say do these packages and then you

give a list of package names, but if you use this then you don't need to update the package list. You can say just say only null marked and it does its thing. So, there are some more examples. Let's see. I provided the null away configuration already or at least I set it up. And now when we run it, so it's basically the same as I showed before.

Yes. Oh, and I also disabled all the other checks. So, with Error-Prone you get already a lot of checks which could be interesting as well. So, if you have and want to get some useful information about that, it's also also very useful to look into Error-Prone. But, if we now do a Maven clean test, it should show something because I left something in there in the nullability

Remember I had this weird constant that So, I I almost forgot about it and now it's now it's back. So, we really need to fix this and let's not do it with a suppress warnings. So, in this case I think it makes sense to have like something like this, right? And then everything should be good. Apart from there's still one place where claiming that there's a null

issue. And it's right. So, it's getting an event and the player was null, which is acceptable for an event. So, a timeout for example event is is you cannot attach it really to a player. So, it accepts it here, but then it fails here. But, this is exactly what the test is about. So, it tries to test whether there's a a null pointer exception. So, this is

perfect. I don't need to change this, but it's annoying that the tool um fails on this. So, the nice thing in IntelliJ, there's this shortcut that you can click and it immediately adds a suppress warnings to your method. I didn't know about this until today, so I learned this today. So, that was awesome. And I think it's very helpful because now if I run the Maven command

again, it should work. And we have a success. So, that's good. Now, if we want to gradually introduce the null marked stuff, let's have a look at this package. We all do package info, right? Are there people who ever wrote a package package info.java or had it generated? One, two. A few hands. We all document our packages, right? Like proper Java doc, etc. Anyways, but it's easy

to generate in IntelliJ. You just do the new and then oh Where did my context menu go? There. And then you do the package info and then it will generate it. Um but you can put the null marked here and it will basically work for the whole package. So, it now marks everything as So, for all these classes, and be aware in Java, there's officially no such

thing as subpackages. Although, they appear like subpackages in in IntelliJ. Um yeah, so the there's no no marked doesn't apply for the event package, only for the for the top-level package in this case. So, if we take a look at what's going on, so we run the Maven command again and it comes up with an issue. And we see that the team here has an issue. It

says something like, "Hey, probably the initialize a method does not guarantee non-null field because this is currently uh non-null. Uh where do you fill in this field?" And indeed, in the constructor, it it's not filled in. And in fact, it's perfect. This should be nullable. I I I I don't want to have the uh description as a required field. It's fine if it's null. The name is

uh something I want and the and the players maybe for a team, but the description is fine if it's uh non-null uh if it's nullable. So, that's fixed, but then we already see the warning here uh that here we have this nullable nullable field and it's returned while actually we're in no-marked code. So, it should not be allowed to return a nullable string unless we mark it

as nullable. So, this is how you how you go through that flow. If you mark it as no-marked and then you find all these problems. And then you check it. Uh there are no nullability issues in this file, so this one is done. and if we now run with Maven, then we see that something else fails. We get a lot of uh problems in the uh in

the nullability test. So, some of them are maybe on purpose like uh this one, but uh yeah, we probably need to look into this. We're not going to do this whole test file right now because then of course might be uh a bit too much, but also interesting to mention that here we're working with a stream we're mapping all the elements of this stream. In this case,

it's a list of Um but um or a a list of referees. Uh what's nice is that IntelliJ also recognizes whether this original list could contain null items. So, if we mark them as null, so if we leave out this check, and if we change this to nullable, so suppose we have nullable elements in the list. Not sure whether it's the best design. Uh so suppose we

have a list like this. Uh it's a it's a list a non-null list, but it can contain null values. And if we go back then here it complains. So we're using a stream on this uh list and now it complains, "Hey, the referee in this case might might be null." So it's a quite quite good the uh the analysis here. I I don't think that NullAway catches

this the situation for Uh and if we it's also nice if we do a null check before or a a filter. Oh, that was a bit uh too aggressive on the keyboard. Let's remove this. Now it sees that we're doing the filter on those elements. We filter out the nulls and now it it knows it's safe, so that's interesting. But as mentioned, so it in your case

could also be that you say, "Okay, I I did fix the implementation, but I don't want to spend time on the on the test right now." So you can just null unmark it and say, "Okay, for the test we don't care." For example. Then there's something interesting with uh generics. So if we look at let's have uh another package. So here we also have another package which

I should also null It has a bit of a weird design which we'll see we'll see shortly. So if we team roster test. So if we look at the problems here we already see, "Hey, there's an issue here. It's passing null." And now when we look at the So this is a roster for the physiotherapist. And the weird thing with the physiotherapist is, "Okay, it's a bit

of a weird design, but a roster could contain multiple coaches, so it's basically a list. But for the physiotherapist, it might be the case that you need to reserve a spot, but so you put in null, but you don't know who is going to fill in that spot. So, there could be a good reason to have null here. So, if you look at the implementation, it looks

like this. So, we have a team roster with a type argument, and then we put it in a list, basically. It's a kind of like a wrapper around the list, a bit richer wrapper. So, suppose now we want to say, "Okay, we want to have nullable elements here." Of course, fix the import, right? Now, it still has an issue. It shows us yellow, so there's a warning

about this because team roster, as mentioned, it says it can have a T, but the T extends staff. And since we're in null marked, this staff type is considered to be non-null. Null is not included. So, now we need to do something, and that something could be like here. We say, "Okay, it could We could also uh nullable staff." So, now you could use either T could

be nullable or non-nullable. You don't You don't care, basically. And then you can make it work. So, now here it works. This one works, and also where we insert the null, it also works. So, that's all fine. But, there's another thing. So, here we're actually testing that that it could be some null here, uh or this case, the physio roster, if it gets staff, then the second

one indeed is null, so that's actually what we expect. And in this case, we know that there's uh there could be a that the first element doesn't have a null value. But the interesting bit is over here. So, there's the assigned staff, and the assigned staff, if we look at the documentation, uh it says it should say in the documentation returns a list of staff members without

the unassigned slots no null entries. So, the documentation isn't really matching our implementation. So, if we look at the implementation, it says, "Okay, no null entries." But, here it's referring to the T type. And remember, T was in this case was a nullable type, which is also mentioned here, like nullable. What you could do is kind of escape out of that. So, we don't want to unmark

this thing. We still want to have the the nullability checks. We could do something like non-null here. So, we get back to the really to the no-marked context. So, now we guarantee that all the Ts that come out of this are non-null. We We already filtered them out. So, if I leave this out, we would show a problem as well. And now we see here that we

can just from the assigned staff, we could do everything basically on those things. So, some interesting things around generics and the nullability. Um but, in fact, like the annotations could be uh specifically, if you look at generics, what do we have there? It's uh over my Display two. Is it so coming from my output? It's an Nvidia. Maybe we can use it to do run some local

LLMs, but uh yeah. All right. Um so, these are the things that we saw, the list of players, nullable players. But, if you look at, for example, arrays, it gets a bit funky because you put the nullable or non-null, you put it in front of the thing that it applies to. uh let's say here, here it means the players can be can be So, the array is

non-null, but the players can be null. but here the array that could be null. So, the players are not nullable. So, this is not null, but the second one, so the array itself could be And then it looks really weird. So, I I I prefer to use lists. at least for this reason I think it's uh it's nicer. Uh but there could be cases that you want

to have nullable stuff in arrays or the other way around. So, you could even have something like nullable player nullable array. So, then you really have uh a nice mix. Uh and also if you have into inner classes like a player.stats object, if you want to make the stats mark that as a as a nullable type, you have to put the nullable behind the period and then

before the inner class. It looks really weird, but that's that's how it how it has to be with uh type use annotations in in Java. So, it's not uh J Specify's fault, I would say. Uh I forgot to mention there was some good news and bad news. I think the good news was that J Specify is really small and very well defined. The bad news is that

can look ugly a bit, but there's even Okay, in this case, some things look okay. Uh but there's even worse But there's even worse news, and that's um it's not perfect because it's all Then we get to run time. Uh we could have things like that. So, so suppose we have a controller and we have a create method on that. So, suppose in this case we're looking

at a Spring Boot annotated method. So, as mentioned, Spring Boot supports the J Specify annotation, so it's uh this is in a null marked context, so that means if Spring Boot sees that request body is not there, it will automatically reject it. So, you won't you won't ever get a request that's null. So, that's good. There uh there's also check on the return type. But, the create

team request, which you see here, which is just a simple record, it contains a nullability annotation. and then you would expect that name is non-null, right? So, if you work with your code, you can just use the name and do whatever you like. But, when Jackson sees this, because I'm assuming Jackson is uh being used as a deserialization library, I think, which is quite common, Jackson doesn't

recognize this. And Jackson uses reflection to instantiate your object, either by using a creator or a constructor or whatever. Um but, it's using reflection, and it doesn't recognize the J specify annotations yet. do a post and you don't specify the uh the name, for example, it will happily do a uh create a create team request with a null name. And then still, your code will fail at

run time. You get And that thing is uh that's uh that's that's something we call null pollution. Uh so, you everything looks nice, unless at run time something weird happens. We have something similar, right, with uh a list, for example, with generics, where you have type erasure. You could think you work with a nicely typed list, but at run time, uh any object could be in that

list. Uh constructed in some other way that didn't in that kind of bypassed uh the compiler. And we will see null pollution in other places as well. So, for example, if someone used the suppressed warnings, because he was smarter than the compiler, he said, "Ah, this will never happen. Suppress warning." Okay, then you have some null pollution. You could have a null field somewhere. But, basically everything

that uses reflection could have if they don't have specific J specify support, you could run into trouble. So, as I mentioned, it was for Jackson, uh the JSON deserialization is the famous one. JPA very annoying because it requires a no arc constructor by default and it instantiates that and it doesn't honor your J specify annotations. So, if you get an entity back from the database, you could

put J specify annotations on it, but it doesn't really do anything at runtime. also, in the documentation or in the wiki of J specify, they mentioned, "Occasionally, a perfect storm will cause a frustrating situation for a user, but this seems like a manageable risk." I think this is kind of an understatement. I think in practice a lot of us use used tools that use reflection somewhere. Uh

so, it's not I wouldn't say it's a perfect storm. Just a storm is good enough, I think, to to mess it up. So, what are your options? So, what you could do is if you're looking at Jackson, uh I saw that in the issue tracker of Jackson is that they were discussing whether they wanted to a J specify support. It didn't make it for Jackson 3, sadly.

Uh but, I think there's still some debate on on the direction. What you could do is say, "Okay, let's consider these DTOs null unmarked. Just unmark them and say, 'Okay, we don't know about the nullability.'" I think that's the most honest approach, but also not very helpful because you want to have that that that null safety in some way. Uh another thing you could try is adding

JSON property required things to the non-nullable fields. So, then you have both the J specify annotation and the Jackson annotation and you need to make sure that they kind of match. Or you could use an open API generator or something like that to automatically generate that. Uh maybe you could do something smart with additional checkers that could check the adjacent properties against the the null null marked

things. what I tried was building a small Jackson customization, so it works for my situation. So, I built an annotation introspector, which is a feature in Jackson which you can inject into an object mapper. What it basically tries to do is look at the annotations and see whether field should be required based on the J specify annotations. Uh so, it worked for my situation, but I don't

think it's it's not really like production ready. So, I'm I'm not releasing it at the moment. but maybe Jackson will come with uh J spec J specify support uh in the near future. Let's let's hope so. But then the other one, GPA, I think it's really a hard fit because of this no arc constructor that's required. so, what I did, uh let's have a look. Oh, let's

not do the firmware upgrade right now. Um so, if we look at the team entity for so, in this case, this was like a normal entity and it's in a null marked An ID, yeah, so what I did is create the constructor that contains all the fields so that when you initialize the this, it works with all the nullability And then have this method available for JPA

to start its thing. But then, null away or IntelliJ will complain that, "Hey, this name field um yeah, you never There is a situation possible when GPA calls this constructor that the name is not initialized. You know that in practice it will always be initialized." So, you could say, "Okay, we have this nullable false here. We can then add the suppress warnings to to make it all

work. It's not very nice, but you can make it work in some way. But, there's always a risk to have a mismatch that you forget about the nullable here, for example, or you forget something here. That's so so that's it's tricky. But, I think the the the common practice would be have a constructor that has all the fields. That's what you use if you instantiate such an

object. At least, that's where you have the nullability checking from your compiler. And then, double triple check whether the the column annotations matching your your nullability, basically. let's see. I think I covered everything. Yes, so typically you use the nullaway in it where you say, "Okay, these fields are not initialized. I know, but they get initialized at runtime at the right moment. Trust me." You might know

about Kotlin that it has some nice things for to indicate that can be null. You just use the question mark, right? Uh so, then it's a nullable type. That's already nice, but in Kotlin you're still dealing with JVM under the hood. You still have the issues in theory with the And if you look at other languages like TypeScript and C#, they also have like really built-in things

for to express that a type can include or does not We don't have that for Java yet, uh but with JSpecify we can get quite a long But, there is a little bit of good news as the JSpecify wiki uh uh puts it. They say, "Okay, there was OpenJDK people, member of the specification group. And if Java ever, one day, has nullable types, the JSpecify standard will

be the only reasonable starting point to base it on." Well, that sounds promising. but yeah, it's not there yet. But, there is something in Valhalla touches upon this topic. So, there's a draft uh JEP. Uh the number is here. You can look it up if you want to. Uh but, they propose in that JEP to have null-restricted and nullable types where they use an exclamation mark for

types that are really null-restricted. So, there's no null allowed, basically what we saw with the non-null. And the question mark for nullable uh types. But, by default, or at least that's what the expectation is, is that by default the if there's no not a question mark or exclamation mark, uh they say it's unspecified, like it is now. That that's needed for backwards compatibility. Maybe they come up

with something default like the null mark, but I'm not so sure. so, after this session in Hall A, there's also a session about Valhalla. That might be interesting, I think. Uh so, Kai Horstmann is doing that session. I think he also touches upon this topic at some point. So, if you want to know more, maybe go there uh after this >> [sighs] >> So, if we look

back, is it really never a null moment with J Specify? I don't think so. I I hoped it when I when I started looking into J Specify. I thought, "Okay, this is the the silver bullet. This is going to fix all my null null moments." But, uh it's not the case. But, it's good for a compile time safely safety. You can apply it gradually, so that really

helps. You don't need to do it a big bang, as we saw with the null mark, null unmarked. You can do it at any level you like, basically. There's no new syntax, just some annotations. Okay, they can be look a bit messy in some some points, but hey, it's okay. But, um yeah, the sad thing is the null pollution, which you still need to be aware of.

So, it's good to keep that in the back of your head. And so, it's basically no Valhalla yet. Not sure when it will come. So, it can be uh a few more years maybe. Um So, whether you're actually working on a hobby project like I was with the core of all application or when you're working on a production application even even more than life is too short

for no pointer exceptions. You don't want to see them. I think that's the bottom line. Sure, it gives an adrenaline rush if you see a no pointer in production, right? Uh but I rather for my production systems I'd rather have dull moments than dull moments. So, with that, thank you so much. Any questions? Remarks, observations over there. So, you had So, you were introducing You fixed some

things for Jackson or you didn't. Mhm. Exactly like like we saw, yeah. Yeah. Yeah, that's a bit hard. So, the question is whether you should also have both like the the validation nullability annotations and the J specify annotations. Maybe for the short term, yes, but I hope that at some point the tools can can complement each other and we could go for J specify, but it it

yeah, it's really like hope based. So, that's not really a a good message. Uh but yeah, it it needs to be adopted by by all the applications or frameworks and I I don't think that the Jakarta team is really the those they were not involved as far as I'm aware in the J specify thing. Uh so, they're not really on that boat yet. So, maybe it will

come, but yeah, so for now I don't have like like a solution right away. Sorry. Any other questions? Yeah, over here. Yeah, so the question is what's the best place to start null marked annotations or start adding null marked annotation? I think it would be best to uh have it as low in your domain as possible. So, if you build it up from your domain, then you

have some safety there and then you build it up in the in the layers on top of it. I think that's that's really a nice thing and particularly because on the edges where you read from database or get the the things in, you still have the null pollution, but if you have your domain at least your domain class like your records, etc. If you have those null

marked and properly annotated, so if it's a lot of code, you can just start with a class, but otherwise I would suggest try it out with a package, add the null marked on the package, see what happens, what falls over. Uh but then just on your domain classes. Start there and then you have some some safety there and then then build it up. Yeah, be so, DTOs

as in the Jackson deserialized ones. Yeah, because of these issues with the no pollution. So, the Jackson can inject if it gets if it doesn't get the field from the JSON, it just put a null there if you don't do anything else. Uh so, you can try to fix that or wait for Jackson to fix that. So, that's why I said, "Okay, maybe not start there because

that's where the hairy bits are. That's where the the trouble is." And if you start really with your domain classes, maybe then there's less trouble around that area while you already get some value of having uh a no mark annotations. Okay. Time's up. Uh enjoy the rest of your conference and thank you.

From event

jPrime 2026

03 Jun 2026 – 04 Jun 2026

All event videos
Back to Watch