Posts Tagged ‘Java’
Interfacing null-safe code with legacy code
When you adopt null annotations like these, your ultimate hope is that the compiler will tell you about every possible NullPointerException (NPE) in your program (except for tricks like reflection or bytecode weaving etc.). Hallelujah.
Unfortunately, most of us use libraries which don’t have the blessing of annotation based null analysis, simply because those are not annotated appropriately (neither in source nor using external annotations). Let’s for now call such code: “legacy”.
In this post I will walk through the options to warn you about the risks incurred by legacy code. The general theme will be:
Can we assert that no NPE will happen in null-checked code?
I.e., if your code consistently uses null annotations, and has passed analysis without warnings, can we be sure that NPEs can only ever be thrown in the legacy part of the code? (NPEs inside legacy code are still to be expected, there’s nothing we can change about that).
Using existing Eclipse versions, one category of problems would still go undetected whereby null-checked code could still throw NPE. This has been recently fixed
.
Simple data flows
Let’s start with simple data flows, e.g., when your program obtains a value from legacy code, like this:

You shouldn’t be surprised, the javadoc even says: “The method returns null if the property is not found.” While the compiler doesn’t read javadoc, it can recognize that a value with unspecified nullness flows into a variable with a non-null type. Hence the warning:
Null type safety (type annotations): The expression of type ‘String’ needs unchecked conversion to conform to ‘@NonNull String’
As we can see, the compiler warned us, so we are urged to fix the problem in our code. Conversely, if we pass any value into a legacy API, all bad that can happen would happen inside legacy code, so nothing to be done for our mentioned goal.
The underlying rule is: legacy values can be safely assigned to nullable variables, but not to non-null variables (example Properties.getProperty()). On the other hand, any value can be assigned to a legacy variable (or method argument).
Put differently: values flowing from null-checked to legacy pose no problems, whereas values flowing the opposite direction must be assumed to be nullable, to avoid problems in null-checked code.
Enter generics
Here be dragons.
As a minimum requirement we now need null annotations with target TYPE_USE (“type annotations”), but we have this since 2014. Good.

Here we obtain a List<String> value from a Legacy class, where indeed the list names is non-null (as can be seen by successful output from names.size()). Still things are going south in our code, because the list contained an unexpected null element.
To protect us from this problem, I marked the entire class as @NonNullByDefault, which causes the type of the variable names to become List<@NonNull String>. Now the compiler can again warn us about an unsafe assignment:
Null type safety (type annotations): The expression of type ‘List<String>’ needs unchecked conversion to conform to ‘List<@NonNull String>’
This captures the situation, where a null value is passed from legacy to null-checked code, which is wrapped in a non-null container value (the list).
Here’s a tricky question:
Is it safe to pass a null-checked value of a parameterized type into legacy code?
In the case of simple values, we saw no problem, but the following example tells us otherwise once generics are involved:

Again we have a list of type List<@NonNull String>, so dereferencing values obtained from that list should never throw NPE. Unfortunately, the legacy method printNames() succeeded to break our contract by inserting null into the list, resulting in yet another NPE thrown in null-checked code.
To describe this situation it helps to draw boundaries not only between null-checked and legacy code, but also to draw a boundary around the null-checked value of parameterized type List<@NonNull String>. That boundary is breached when we pass this value into legacy code, because that code will only see List<String> and happily invoke add(null).
This is were I recently invented a new diagnostic message:
Unsafe null type conversion (type annotations): The value of type ‘List<@NonNull String>’ is made accessible using the less-annotated type ‘List<String>’
By passing names into legacy code, we enable a hidden data flow in the opposite direction. In the general case, this introduces the risk of NPE in otherwise null-checked code. Always?
Wildcards
Java would be a much simpler language without wildcards, but a closer look reveals that wildcards actually don’t only help for type safety but also for null-safety. How so?
If the legacy method were written using a wildcard, it would not be (easily) possible to sneak in a null value, here are two attempts:

The first attempt is an outright Java type error. The second triggers a warning from Eclipse, despite the lack of null annotations:
Null type mismatch (type annotations): ‘null’ is not compatible to the free type variable ‘?’
Of course, compiling the legacy class without null-checking would still bypass our detection, but chances are already better.
If we add an upper bound to the wildcard, like in List<? extends CharSequence>, not much is changed. A lower bound, however, is an invitation for the legacy code to insert null at whim: List<? super String> will cause names.add() to accept any String, including the null value. That’s why Eclipse will also complain against lower bounded wildcards:
Unsafe null type conversion (type annotations): The value of type ‘List<@NonNull String>’ is made accessible using the less-annotated type ‘List<? super String>’
Comparing to raw types
It has been suggested to treat legacy (not null-annotated) types like raw types. Both are types with a part of the contract ignored, thereby causing risks for parts of the program that still rely on the contract.
Interestingly, raw types are more permissive in the parameterized-to-raw conversion. We are generally not protected against legacy code inserting an Integer into a List<String> when passed as a raw List.
More interestingly, using a raw type as a type argument produces an outright Java type error, so my final attempt at hacking the type system failed:

Summary
We have seen several kinds of data flow with different risks:
- Simple values flowing checked-to-legacy don’t cause any specific headache
- Simple values flowing legacy-to-checked should be treated as nullable to avoid bad surprises. This is checked.
- Values of parameterized type flowing legacy-to-checked must be handled with care at the receiving side. This is checked.
- Values of parameterized type flowing checked-to-legacy add more risks, depending on:
- nullness of the type argument (
@Nullabletype argument has no risk) - presence of wildcards, unbounded or lower-bounded.
- nullness of the type argument (
Eclipse can detect all mentioned situations that would cause NPE to be thrown from null-checked code – the capstone to be released with Eclipse 2020-03, i.e., coming soon …
Oracle made me a Stackoverflow Guru
Just today Oracle helped me to become a “Guru” on Stackoverflow! How did they do it? By doing nothing.
In former times, I was periodically enraged, when Oracle didn’t pay attention to the feedback I was giving them during my work on ecj (the Eclipse Compiler for Java) โ at least not the attention that I had hoped for (to be fair: there was a lot of good communication, too). At those times I had still hoped I could help make Java a language that is completely and unambiguously defined by specifications. Meanwhile I recognized that Java is at least three languages: the language defined by JLS etc., the language implemented by javac, and the language implemented by ecj (and no chance to make ecj to conform to both others). I realized that we were not done with Java 8 even 3 years after its release. Three more years later it’s still much the same.
So let’s move on, haven’t things improved in subsequent versions of Java? One of the key new rules in Java 9 is, that
“If [a qualified package name] does not name a package that is uniquely visible to the current module (ยง7.4.3), then a compile-time error occurs”.
Simple and unambiguous. That’s what compilers have to check.
Except: javac doesn’t check for uniqueness if one of the modules involved is the “unnamed module”.
In 2018 there was some confusion about this, and during discussion on stackoverflow I raised this issue to the jigsaw-dev mailing list. A bug was raised against javac, confirmed to be a bug by spec lead Alex Buckley. I summarized the situation in my answer on stackoverflow.
This bug could have been easily fixed in javac version 12, but wasn’t. Meanwhile upvotes on my answer on stackoverflow started coming in. The same for Java 13. The same for Java 14. And yet no visible activity on the javac bug. You need ecj to find if your program violates this rule of JLS.
Today the 40th upvote earned me the “Guru” tag on stackoverflow.
So, please Oracle, keep that bug unresolved, it will earn me a lot of reputation for a bright future – by doing: nothing ๐
Edit: In 2020/04, Oracle dropped any plans to resolve the issue, meanwhile my answer on stackoverflow matured into a “Great Answer” with more then 100 upvotes.
Jigsaw arriving – and now what?
The debate is over. The result of fierce discussions has been declared. Jigsaw is knocking at the door. Now the fate of Jigsaw depends on the community. Will the community embrace Jigsaw? Will we be able to leverage the new concepts to our benefit? Will we struggle with new pitfalls?
Let’s step back a second: do we really know what is Jigsaw? Do we really understand the design, its subtleties and implications?
At EclipseCon Europe 2017 I will try to shed a light on some lesser known aspects of Jigsaw. A deep dive into things I learned as a JDT committer while implementing tool support for Java 9.
In search for truth
To set the stage, we’ll first have to figure out, who or what defines Java 9ย โ or Jigsawย โ or JPMS. This is both a question of specification vs. implementation as well as a matter of a specification spread over several documents and artifacts. Let’s try to grok Jigsaw from the legally binding sources, rather then from secondary writing and hearsay (if that is possible).
We will have to relearn some of the basic terms, like: what is a package in Java? Do packages form a hierarchy? (I will show, how both answers, yes and no, are equally right and wrong).
Jigsaw is said to do away with some “undesirable” stuff like split packages, and cyclic dependencies. Really? (Yes and no).
Encapsulation
Of course, with Jigsaw all is about encapsulation โ easy to agree on, but what is it that a Java 9 module encapsulates? Only a deep understanding of the flavor of encapsulation will tell us, what exact qualities we gain from following the new discipline (it’s not about security, e.g.), and also what pains will be incurred on the path of migrating to the new model. (Hint: I will be speaking both about compile time and runtime).
Interestingly, the magic potion Jigsaw also brings its own antidote: With tightening the rules of encapsulation, also the opposite becomes necessary: they call it breaking encapsulation, for which I once coined the term “decapsulation“. I should be flattered by how close Java 9 comes to what I call “gradual encapsulation“. So, the talk can not stop at presenting just the new language concepts, also the various extra-lingual means for tuning your architecture need to be inspected through the looking glass. This is also where tools come into focus: how can JDT empower its users to use the new options without the need to dig through the sometimes cryptic syntax of command line parameters?
Loose ends
At this point we shall agree that Jigsaw is a compromise, fulfilling many of its goals and promises, while also missing some opportunities.
I will also have to say a few words about long standing API of JDT, which makes guarantees that are no longer valid in Java 9. This raises the question: what is the migration path for tools that sit on top of JDT? (There is no free lunch).
Finally, it wouldn’t be Java, if overloading wouldn’t make things worse for the newly added concepts. But: haven’t you become fond of hating, when JDT says:
The type org.foo.Bar cannot be resolved. It is indirectly referenced from required .class filesWe may be seeing light at the end of the tunnel: for Jigsaw we had to revamp the guts of our compiler in ways, that could possibly help to – finally – resolve that problem. Wish me luck …
Hope to see you in Ludwigsburg, there’s much to be discussed over coffee and beer
![]() |
Ludwigsburg, Germany ยท October 24 – 26, 2017 |
Object Teams in the times of Eclipse Luna and Java 8
With Eclipse Luna approaching milestone 5 it’s time to write a bit about what this year will bring for Object Teams.
Overhaul of the OT/Equinox weaver
The Luna cycle started with a surprise (for me): Equinox no longer provides the Adaptor Hooks, which allowed OT/Equinox to hook into the OSGi class loading process and perform its bytecode weaving. Outch!
On the other hand, OSGi meanwhile has a standard WeavingHook for doing this kind of stuff. With some efforts (and lots of help from Tom W. – no help from p2, though) I managed to migrate OT/Equinox to this new standard. At the end of the day, this will bring better encapsulation and hopefully better launch performance, too. Details to be posted some time later.
Lift OT/J to be based on Java 8
Some may have noticed that most my Eclipse-time is currently being spent in helping JDT to get ready for Java 8. And sure this is an exciting endeavour to be part of!
For Object Teams the cool part about this is: I’ve seen the changes for Java 8 happen in JDT, which greatly helps to adopt these changes for OT/J. Once in a while this even reveals a bug in JDT before it ever surfaced ๐
The integration of OT/J with Java 8 still has some regressions, but my plan is to have this at good “milestone quality” when Java 8 is released in March and to get it to full release quality for Luna GA in June.
Question: does it actually make sense to combine lambdas and type annotations with roles and teams? I strongly believe it does, because these are language improvements in entirely different categories:
- Lambda expressions help to implement algorithms in a more concise and also more modular way – this lets you think differently about functions.
- Type annotations help enhance safety when used together with better program analysis (like, e.g., enhanced null analysis) – these let you think differently about types.
- Roles and teams help improve the program structure at a larger scale – these let you think differently about classes and objects.
So, if each of these additions makes sense, then combining them all in one language will certainly yield a very powerful language. Some examples of combination to follow as we approach the release.
ObjectTeams.org is down, long live ObjectTeams.org
Not all material from the 10+ years of history of Object Teams has moved to Eclipse.org. Notably the language definition (OTJLD) and scientific publications are still hosted on objectteams.org. Until recently, my former University kindly provided the host for publishing these research results. Now that server has gone off service and for a while objectteams.org was dead — but as of today the relevant content is back online – sorry for the disruption. And, btw, this is also the reason why this blog has changed its URL.
Please wish me luck for the work ahead, both on JDT and Object Teams ๐
