Nate Black talks with Nicolai Parlog about Java 9. Topics include: a timeline of Java features; new patterns enabled by Java 8 lambdas, default interface implementations and how they enable code evolution; how Java 9 takes this further with private default methods; an introduction to Java modules: the Java Platform Module System (JPMS); “launch time” dependency validation; module “requires” and “exports”: documentation as code and a new topic for code reviews; how to migrate an existing codebase to Java 9 and modules; benefits of Java modules: reliable configuration and a smaller Java runtime; the new Java release schedule.
- Nicolai Parlog’s bio page
- Nicolai’s book, The Java Module System
- The Ultimate Guide to Java 9
- Java 9 Migration Guide
- Java Module System Tutorial
- Nicolai’s YouTube channel
Transcript brought to you by IEEE Software
Voice Over: This is Software Engineering Radio, the podcast for professional developers on the Web at SE.radio.net. SE Radio is brought to you by the IEEE Computer Society and by IEEE Software Magazine, online at computer.org/software.
Nate Black: This is Nate Black for Software Engineering Radio. My guest today is Nicolai Parlog. Nicolai is writer, speaker, teacher, and consultant covering topics like Project Jigsaw, Java 9, JUnit 5, clean comments, and Java in general. Nicolai is the author of the forthcoming book The Java Module System. Nicolai, welcome to Software Engineering Radio.
Nicolai Parlog: Thank you very much. Hi.
Nate Black: In this episode we are going to cover what’s new in Java 9. The highlight of Java 9 is the new module system and I promise that we’ll get to that. But first, Nicolai, you said that many of the Java 9 features build on Java 8, –
so could you please explain why Java 8 was such a significant milestone for Java?
Nicolai Parlog: To answer that it makes sense to go back in time a little bit and look at the Java release history. So, in 2004 Java 5 was released. It had generics. It was a big change to the language, a big change for the ecosystem, and it really improved coding with Java a lot. So, that was ten years before Java 8.
In the meantime, in the ten years between those two releases, not that much happened. Java 6 was mostly small changes and Java 7 too. I’m talking about the language here. There were API changes and improvements of the standard library and things like that, but the language itself hardly changed. And so, by 2012, 2013, it was like eight, nine years, coming up to a decade of basically no change. I myself started coding somewhere in 2006 with Java and I didn’t even notice that Java was changing at all. I just never realized that there were new major version releases because nothing ever happened.
And I think that was also the time where I got the feeling that people think Java is dead and nothing is happening and it’s just – it’s going to be a small, downhill decline from here till its eventual heat death.
And then, 2014, Java 8 came out. For me, that was a significant change, and I think to everybody else as well. Having lambdas and streams in the language, particularly lambdas, made programming with Java fun again. It was a new thing to learn and a new thing to study and it brought all of the Java community much closer to functional programming, not necessarily because it’s now a functional language – which it isn’t – but you could use some paradigms, you could use some ideas of functional programming. And so, we started to study that and we started to learn about functional programming, and little libraries crept up because of that. That was a fun release and a fun time to work with Java and learn new stuff.
Nate Black: Let’s talk a little bit more about lambdas. Could you please explain what lambdas are?
Nicolai Parlog: Sure. Okay. Let’s say you have a bunch of numbers. In Java, maybe I would say it’s a list or a set or whatever, but you have a bunch of numbers and you want to add them up. So, what would you do? You take the first two numbers and add them, then the third and you add them, and then the fourth, until you’re done, right?
So, you write that code and you’re done. And then, somebody comes along and says, “Now we need to multiply numbers.” And then you write the same code again with the multiplication. And then you realize, “I just did the same thing twice. I iterated over the whole list, collection, whatever. Took the first number, did something to it, then took the result and second number, and so forth” So, most of the code is the same. The only thing that changed was the operation that I did. I threw two numbers into an operation – addition or multiplication – and then I got a result out. So, why not refactor that, to say, “I do the whole loop thing, and you give me the operation that I want to do.”
So, Java programmers have been doing that a lot before Java 8. And what we usually do is we take an interface. Maybe we can call it operation, I don’t know, S1 method called “do it” or “operate” or “compute.” It takes two numbers, two ins, whatever, and returns another number. So, now you have this interface.
And when I call that method I say “compute” and then I give you the implementation of the interface that either adds or multiplies or does something else. And this has been a pattern that has been around in Java for a long time. We use an interface with a single abstract method, and at the place where we call a method that needs interface implementation we just create an anonymous class. In that place we don’t necessarily have an implementation lying around; we create it in that place. We say “new interface” and then we implement that method. And that’s, like, half a dozen lines.
And it’s a little weird language feature to create a class ad hoc in the middle of some other code, and it takes surprisingly many lines for just a small operation, for just a plus. And what lambdas do in Java is they just allow you to create this interface much, much more succinctly. You just say “Okay, it’s a single method,” and a single method is basically like a function. You put things in and you get things out. So, why not have a much shorter notation for that?
So, you still use everything else as just the same.
You use still this interface, which is now called a functional interface because it only has one method. And then you – when you provide implementation of that you don’t have to do the whole anonymous class dance with the whole method body and method signature in there. You just go (aba+b). So, that would be a lambda, like a very short version of that whole long class.
Nate Black: So, prior to Java 8, if you wanted to program in a functional style where you’re passing functions around, you would have to create an anonymous class, where all you really wanted to do was create an anonymous function. And the Java 8 lambda syntax allows you to do that in a really succinct way.
Nicolai Parlog: Yeah, so for this specific approach to organizing code, to pass-in functions, Java 8 changed a lot of codes. Because before that the interaction was so high and the effort that you had to put in, the boilerplate that you had to put in was so high that it wasn’t really fun programming that way.
So, you only did it under, let’s say, just extreme circumstances. It was not something that you did – like, you would not – it would not be the first solution to the problem because, being honest, it sucks looking at this big block in there, big block of code in the method call.
So, people didn’t do that as a first approach, even though it’s a very good approach. It just happens to be very wordy in Java 8 – sorry, in Java before Java 8. And so, when Java 8 came out you could do the same thing earlier. But there’s a great talk by Lina Stallworth where he explains git, and he says that when something is faster, especially if it’s way faster, it means you’re not just doing the same thing faster; you’re going to change how you use that thing. And this is what Java 8 actually did. It not only made these specific call sides shorter, which is a small gain, it taught us that we can use this approach and we can use lambdas much more freely than we would use anonymous classes before.
So, what I just described, the whole operation, it’s called “reduce.”
And the new stream API that came with lambdas makes use of that and has that operation already. So, these kind of APIs would not have been feasible before, and now there are APIs building on that and using lambdas – APIs that would not be, frankly would not be usable without lambdas. So, it did not only change a small detail in the language and made it a little bit less wordy, it offered us a new tool to make new solutions which are much more fun to use and much more fun to provide.
Nate Black: In episode 266 of Software Engineering Radio we discussed the JVM, the Java Virtual Machine, as a language platform. That show talked about compiling languages other than Java to bytecode that can execute on the Java Runtime, the JVM. So, we could think about changes to the Java language and standard library as separate from changes to the bytecode and the runtime. Which of these things are changing when new versions of Java come out?
Nicolai Parlog: So, with Java 8 that’s actually a pretty interesting question, because to implement lambdas the JDK team went through various different options, and in the end they settled on something called invokedynamic, which I think you also discussed before. That was introduced in Java 7 and it’s a way to express in bytecode that we don’t know yet what happens; let’s see at runtime – which is very un-Java-y. Normally, we say at compile time and then it’s enshrined into bytecode which method to call, what parameters, and other kind of stuff.
That didn’t work for lambdas, so they used invokedynamic to basically say, “Look, somebody put this lambda here. Find out at runtime what to do with it.” This feature already existed in the bytecode when Java 8 came out and the JVM could already handle it. So, for that specific change the JVM did not have to change in any significant way.
But there are other improvements in Java 8 which had to make these changes. So, usually, when a new Java version came out – in the past it was every couple of years – all of these things would change. You would get a new bytecode version.
You would get new bytecode commands in there that old bytecode analyzers could not necessarily work with and would have to be upgraded – would probably need to be upgraded to new language features, of course, and standard libraries made then – made use of these new features. These things are separatable, but I don’t think that a big Java release would only see a change in one of these. Usually, they saw changes in all of them. But of course you had some things which would just touch on individual aspects. For example, performance improvements would usually just be visible in JVM. Of course, if you just make a language change, then you don’t necessarily have to change libraries. But since Java came out every couple of years enough changes amassed that in the end every release saw changes in all of these areas.
Nate Black: Before we discuss the java module system, let’s talk about some of the other changes you thought were interesting in the Java 9 release.
Nicolai Parlog: So, there are small language changes in there as well. The module system itself is – it’s a real language change. And as you said, we’re discussing that later. Let’s just talk about what kind of Java syntax I can write now that I couldn’t write in Java 8. And there are a few small things.
For one, if you didn’t use Java 8, then you don’t know that, but interfaces can now have methods with a method body. In Java 8 you can create a method with its implementation and put it into an interface. It’s called a default method. It was there for compatibility reasons to allow to improve the existing collection API to do something with streams without breaking all implementations. Long story short, you have methods and interfaces now.
But all of these methods used to be public and part of the API. Then people found out that, hmm, with default methods and interfaces, they’re not only good for compatibility, you can actually do some nice things with these. You can extend an interface with a couple of nice features. And so, people started using them not only for backwards compatibility, and with more use we realized that we sometimes want to share code between these. And so, in Java 8 the only way to share code between default methods is to have another default method, which is also public, also part of your API. Maybe you didn’t want that.
Java 9 now allows private interface methods, which is just a regular private method like in any other class. It cannot be overridden by the implementing class.
It has to be implemented and can only be called from the interface itself. But it’s a way to share code between default methods.
Nate Black: You said that the default methods are good for compatibility. Can you please explain how they are good for compatibility?
Nicolai Parlog: Imagine you were developing the JDK and you were developing in Java 8 a great new stream API that does awesome things. So, a stream API basically allows you to have a bunch of objects, and then you use lambdas to say first for a person, get the orders. And for the orders, get the addresses. And then, for the addresses, map it to a nice string and then log it or whatever. In Java 7 that would have been a for loop. In Java 8 you can use a stream API for such operations. But how do you get to the stream API. It would have been great if the operations are just there on collection.
So, they decided to not put all the operation on collection, but at least have on collection – like the collection interface – have a method called stream that will return the stream and then can do all these shenanigans. But as soon as you as the JDK developer put the method stream on collection –
you just broke dozens of high profile libraries out there who all have their own implementation of collection and who don’t have that method, so they all break.
So, default methods mean you can have a new method in interface that brings its own body and if you – so, in that scenario, you are the guy who’s developing the JDK, right? So, I am developing a library which implements the collection interface. You ship a collection interface with the new method, and my implementations inherit that method. So, now my implementations also have a stream method even though I didn’t do a thing. I can override your implementation with something that suits my class better, but I get your implementation for free without breaking my code. So, that was the first idea: have this for compatibility reasons.
And then, we realized there are some other things we could actually do with it. For example, on iterator – I don’t know how many people implement iterator – so, iterator has a remove method, and usually nobody wants to implement it. So, we all implement iterator remove and then it throws UnsupportedOperationException.
And we don’t have to do that anymore. So, since Java 8 the remove method on iterator is a default method that throws its own UnsupportedOperationException. So, when I implement that interface I can spare these couple lines of code.
Nate Black: I as a JDK developer, somebody working on the Java standard library, would like to add a new method to the collection interface. But before default methods became available in Java 8 this meant that you as a library developer would not be able to compile your code anymore because you hadn’t implemented that new method, in this case the stream method, on your collection.
Nicolai Parlog: No, that’s exactly right. So – and it’s even worse. So, not only does it mean my code doesn’t compile anymore – me as the poshmark who is the library developer – not only does my code no longer compile because I don’t implement that method, also, if you use my library in Runtime you will get a nasty error because – not you, you’re the JDK developer – so, the third person, she’s using –
my library and she sees, “Oh, this implements collection. Collection has that new method.” So, she calls stream on my library because collection interface has that method. So then, if she uses the version that I compiled against an old Java version, which – so my version, my implementation of my library worked fine – she has no inclination why my library would not be usable with Java 8 unless she reads the docs. But nobody reads the docs.
So, she uses it with Java 8, of course, and then she calls that method, and in Runtime she gets a nasty error crashing the whole program, because of course there is no stream implementation on my specific implementation of the collection interface.
Nate Black: Were there a few more language changes in Java 9 that you wanted to talk about?
Nicolai Parlog: Yeah, there are two more. Again, they are small changes, but they are nice to have. Since Java 7 you have try with resources, where you can say “try” and then in parentheses you say, “This is the thing that I want to try something with.” Maybe it’s a stream or something. It’s definitely something that needs to be closed at some point. So, instead of me doing it at the end of the try block saying, “I want to close this,” –
and then at the end of the catch block I also close this, I just leave it up to the language. I say “try” and then in parentheses I use the thing that I want to try something with, and then Java guarantees that whichever path I take out of this try block, either the regular path or the exception path, somebody will call close on that variable.
And in Java 7 I had to assign that variable. There had to be assignment in there. There was a good reason for that; it was a sort of technical reason. So, there was a reason why I had to write “try” and then in parentheses “input string FOO equals whatever.” And I had to make this an assignment for really no very good reason. And in Java 7 I don’t have to do that anymore. So, if I get an input string from somewhere else I can just try FOO and then do my trying, which cuts a little bit of that boilerplate out of my code.
And the same is true for the other improvement, diamond operator on anonymous classes. Another thing where we had a boilerplate – or, not a boilerplate, but let’s just say we had more code than we absolutely had to.
When you create a list of string – it’s a new array list and you can use the – was it the angle brackets, I think, which are empty. You don’t have to say “string” again. You don’t have to “list string is a new array list of string.” You can just say “list of string is a new array list” – angle brackets – and the complier is clever enough to know, hmm, maybe it’s trying to get an array list of string, so I’ll just fill it in there.
If on the right-hand side you don’t just create – instantiate a regular class but instantiate an anonymous class – so, which means you have open curly brackets and then do something, create in that place an anonymous class – then you couldn’t use the angle brackets. Because, again, in this case there’s actually a technical reason for that. I’m also not going to go into that, but there was a good reason to do that. But I found a way to improve the compiler in that regard that it can handle more cases there, so now in most cases we don’t have to put the type in there.
In some very specific cases, if you do a lot of shenanigans with generics it’s very complicated. You have to still put in the type. But usually you don’t. And the JDK team estimated that having the diamond operator in the first place, the regular one, cut down 90 percent of these duplicate type declarations. And allowing the diamond operator on anonymous classes in Java 9 cut down another nine percent points off the ten percent that remained. So, only one percent of these duplicate type declarations remain and they still have to be in there. So, for technical reasons they have to be there because the complier otherwise guesses a type which the JVM doesn’t understand, so we have to help it in putting in the exact type that we need.
Nate Black: So, it was an aesthetic change but it also reduced redundant unnecessary code.
Nicolai Parlog: Yeah, definitely. Yeah, it was – so, it was an aesthetic change, exactly, to spare this assignment. But the other problem that it solved was actually naming, which we all know is one of the hard problems in computer science, because supposedly you already have a perfectly fine –
variable name for the thing that you have, but if you have to assign it, then you need another variable name. But it’s still the same thing. So, it’s always tough to come up with the name. Usually, now you just come up with “Well, now it’s just an F for FileInputStream” or whatever. So, if you have already a good name, then the really annoying part is not only the assignment that looks useless, it’s also “What is this new variable’s name?” And it kind of gives you pause when you write the code. And now you just use the same variable and you’re fine.
Nate Black: The big change in Java 9 is the introduction of modules. How do you like to explain modules to people who know Java perhaps but are new to the concept of modules?
Nicolai Parlog: The matter that I use is the one that I had in my head all along, even before modules arrived at the scene. So, when I think about code – I think everybody does that slightly differently, but when I think about code I basically have a huge graph in my head. You know, when I have – let’s say I have a class. So, what does this class do? It usually calls other classes. So, in my head I have a – this is a class, which is like a bubble, and then these other classes, which are also bubbles, and then there are arrows because they’re calling each other.
Right? I have these bubbles, and if you took computer science you would call them nodes. And then you have edges, which are the arrows, which call each other. And you have that for classes, for example.
But not only for classes. You can go lower. You can say, “I have the same thing for methods, because methods call each other.” And you can go the other way as well. You don’t have to talk about classes; you can go to packages. Or you can talk about JARs.
The graph also looks different at compile time and at runtime. It’s not an exact science. But it’s, I think, an idea that many people have – that I have in my head about the code that I write. How does the code relate? How does one thing call another thing?
And in this graph these things have a couple of properties, like every node has a name, a method name, a class name, a JAR name, a package name. They have dependencies. If you talk about methods, it’s more method calls. But they use each other, so they have these arrows of dependencies. And then, not immediately obvious but there is – each of these bubbles has something that I need. I wouldn’t call another method just for the fun of it. Usually, it does something that I need. So, that’s the third property.
Beyond name and dependency, it also has something that I want to use – let’s call it an API. These three things exist on all these levels. But let’s just stick to methods, classes and JARs.
For methods and classes the JVM shares our understanding. The JVM says, “Yeah, it’s a class. It has a class name. And yeah, sure, it has an API, which are the public methods. Yes, it has dependency. You can actually scan the bytecode and look for what other classes does it relate to.” So, on the level of classes and also of methods, method level, the JVM sees things like we do. And this is good because it means that there’s no friction between the two of us.
But on JAR level that’s not the case anymore. You cannot say, “This JAR depends on that other JAR, and only if that other JAR is there, then I want to launch,” for example. JARs don’t have names, which, for example, when you track down complicated runtime errors you sometimes see a stack trace and wonder “Which JAR is this class in again?” But you don’t know. It was just loaded from a JAR. See, for English speakers maybe it’s easier to see why JARs are so useless because it’s really just a jar.
It’s just something that you take something out of, and at runtime you just take all the cookies out of the jar and leave the jar back on the table, possibly broken. The JAR is just a container. It has no identity. And this makes all kind of problems that we’ve just gotten used to. We use Maven or Gradle or other tools to provide us all the dependencies because we don’t – we have no other way to find out at runtime whether everything is there. And for public APIs we don’t even have any good solution.
At runtime the JVM comes, allots all of these classes, all of these JARs, and puts them all into one big ball of mud. Whatever thing we had in our mind about one JAR using another JAR’s API, that’s all fiction. It’s all just a bunch of classes running the same environment. And everything’s that public is fair game. So, even if as a library developer I said, “This package is internal,” the JVM doesn’t care. “I can just call whatever I want.” So, that’s the situation we’re in.
Now Java 9 comes out, and what it basically does is it takes JARs and says, “Look, you now have an identity. You now have something that I understand.”
Nate Black: For the benefit of our listeners who aren’t Java developers, can we describe a JAR as just a ZIP file that contains the class files which are the combined bytecode of our Java classes?
Nicolai Parlog: Yeah, that’s exactly what it is. It’s really just a ZIP file. So, whenever you have a JAR and you don’t know how to get in, just rename it to ZIP and there you go.
Nate Black: There are a few terms floating around out there for Java modules. We have JPMS, the Java Platform Module System; Java modules; and Project Jigsaw. Could you please briefly explain what are the differences, if any?
Nicolai Parlog: When Mark Reinhold back in 2008 said he wanted to have modules – not only him but – so, we wanted to have modules, he created what’s called Project Jigsaw. And Java is developed in such projects. So, whenever you had JDK teams work on something new they create a new project, which essentially, if the entire Java code –
would be in once single repository, which I think it’s moving towards now, but it wasn’t back then, you would call it just a branch. It’s just a feature branch. So, you would just create a branch off the current version of Java and then just work on the new thing in the branch. And Project Jigsaw had the goal to provide a specification and an implementation of a module system that was geared towards the JDK but also usable by user code, file code. So, that’s Project Jigsaw. And it started, I think, in the end of 2008 and then went all the way to 2017. And Mark Reinhold is very, very happy, and very publicly happy on Twitter where he can finally tick that box and say, “It’s done,” because it took years out of him.
So, that is Project Jigsaw. It produced the Java Platform Module System, the JPMS, though it’s – I think almost nobody calls it that because it kind of – it’s a mouthful. And so, usually people just say “modules.” But it makes sense to occasionally point out which modules we’re talking about because Maven knows modules, –
IntelliJ knows modules, OSGI knows modules – well, they call it bundles, so at least there’s no conflict there. So, it occasionally makes sense to say, like, “Now I’m talking about JPMS modules,” because another thing, of course, module is like object-oriented design term, which is not necessarily exactly the same. So, occasionally it makes sense to say, “Now I’m talking about JPMS modules.” But usually, people just say “modules.” And when they talk about the Java module system, they mean – most likely mean the JPMS because the mother module systems have specific names, like OSGI.
Nate Black: For the programmer, what are the benefits of modules?
Nicolai Parlog: We have something that’s called JAR hell class path here. We kind of got used to it. But it means that the JVM doesn’t understand dependencies, particularly transitive dependencies, so I have to hunt them down manually. Of course, we build great tools to solve that for us, but still, technically it’s a shortcoming of the JVM. So, something could be missing at runtime, for example, and you wouldn’t find out until it’s too late.
And then, you also have version conflicts, so if you have two versions of the same library that you absolutely have to use because transitive dependencies, one of your immediate dependencies uses, let’s say, Guava 19 and the other one uses Guava 14 and no way can they both run on the same version, then yeah, that’s a problem. Spoiler: It’s still a problem. So, just – if you listen and get their hopes up, now they should. Still, it’s a component of JAR hell, so we have that. That was part of the problem.
And the other part is that we had no encapsulation across JARs. So, as I said earlier, every public type is fair game, free to be used by everyone. Then, the JDK itself contains some security-relevant code that not just everybody should be calling. So, what they did is they put in a security manager, which you have to activate, and then the security manager is on critical code paths and checks whether this access is allowed.
The problem with that is that it’s a manual process. You cannot automatically put in all the places because if it’s in a hot loop or something, you couldn’t make that check every time.
So, it has to be put into the right places to not impact performance too much. Even then, anecdotes seem to suggest that turning security manager on means 10, 15 percent less performance. And still, you want to put it in the right places to not make that 50 percent, maybe. But that means it’s a manual process. And when Java 8 was delayed due to security problems, I think two of the five major security breaches that Java had were exactly missing security manager calls. And the reason for that is that Java, JVM itself could not understand that this code is not supposed to be called by the user. This code is only to be called by other parts of the JDK. So, because the separation that we talked about on the level of libraries also didn’t exist in the JDK, and so through the JDK every call would be indistinguishable, so you had to put in manual checks: “Is this call coming from code that’s allowed to make that call?”
So, this manual security was a problem.
And then, last – back then, last but not least – now maybe it’s least; who knows – Java is a monolith. You had just one JVM, just one Java runtime, that it’s all or nothing. And that was rather big. And I think in the meantime memory got so much cheaper that I wouldn’t think that it’s that that strong of an argument anymore, but with doc images and other virtualization it comes back – the idea comes back to maybe have a smaller runtime, like only have the stuff in there that I need. Like, why would I like a runtime that has 80 MB, half of which is probably Swing and AWT, which I never use – I’m just using a wetback end. So, why have that in the runtime all the time? Why would I need that? So, maybe have a runtime that can be split apart.
And that was like – so, class path hell, no encapsulation, the security problem, and the rigid Java runtime, I would say those were the big problems that were envisioned the module system could tackle.
Nate Black: I’d like to understand how modules and Java 9 address some of those problems. But first, let’s talk about at a high level how modules work.
Nicolai Parlog: There’s a one-to-one relationship between JARs and modules. What is this module’s name? What other modules does it need? And what is its API? And the API part is not that important at the moment because we’ve largely talked about dependencies.
And the JDK itself got split up into about, like, 100 modules. I think 20 or 30 for them, 20 or 30 of them are publicly supported and standardized platform mod like, let’s say, implementation details, to oversimplify. So, we’re mainly using those 20, 30 modules.
What these modules do, as I said, they have a name and they have dependencies that means a variety of things. First of all, it means that when the module system is in play it can make sure –
that all dependencies are present, all transitive dependencies are present. It actually enforces this. It will not let you launch – if you say, “My library uses Spring and Spring uses, OkHttp and I don’t know, somewhere there’s also Guava maybe, and then, OkHttp is missing or Guava is missing,” even though it’s not my direct dependency the JVM would not let me launch because something is missing. So, it understands the concept of this dependency graph of there are nodes, and if they are gone, then something is not right, so it tells me that they are missing. That’s – so, that solves the first part of the problem where some stuff is not there.
When we talk about version conflicts, then – we just talked a lot about that – you cannot have two JARs that claim to be with the same module. If you do, you get a compiler runtime error, depending on what happens. Sorry – not runtime; launch time.
I invented this new thing; I call it launch time. In the book I write a lot about it. It’s not really a Java concept, but what I mean is technically it’s at runtime but it’s at the beginning of runtime, not like an hour into your program. The first time you do, I don’t know, some – you do some backup service that runs as part of your system, the first time you do it is an hour into the project – I’m sorry, an hour into the program run, and then it realizes something is missing and it crashes. But at launch time it checks these things.
So, when you have Guava 14 and 19 on the module path, which is like the class path essentially but for modules, then it will not launch. It will say, “Look, you have the same thing twice. This doesn’t look right.” But talking about versions, the module system does not have any notion of versions. Right? You can put in version information – it’s kind of like meta information – into the module descriptor, but that’s just for our – that’s just for us, just for debugging, maybe to keep track of what exactly went into this application. It’s nothing that the module system actively works with.
So, if one of those two JARs says “I’m Guava 14 and I’m Guava 19” – actually, if they want to say that, they can. They just say, “I’m Guava” and then you have the same thing twice and then it doesn’t work. So, the module system does not understand versions and does not actually help you with the version conflict thing. It just means you find out at launch time, but there is not solution in there. You could even argue that because of the module system complaining, a solution like the Hail Mary, or if I just put both on there, that doesn’t work anymore. So, you could even say that one path to solving a version conflict is now closed if you use the module path.
And then, another problem which – so, we talked about version conflicts but we also talked about maybe there was a fork, maybe it’s just – it’s not – it’s a different module, right? One says, “I’m Guava” and the other says, “I’m whatever other – whatever the other fork is called,” so they’re not the same modules. But they still contain the same packages. Then, what you contain is a so-called split package situation where the module system says, “Look, each package can only be contained in one module.”
First of all, that makes a lot of sense. Second of all, it’s a useful optimization when loading classes. So, this means that you cannot have Guava and its fork on the class path – sorry, on the module path as long as they contain the same packages. So, you get an error then as well.
Nate Black: If we have dependencies or transitive dependencies that are using different versions, conflicting versions of the same library, how can we get past this problem?
Nicolai Parlog: When we talk about taking an application that works in Java 8 – right? – that’s the assumption, it works in Java 8, now we want to make it work in Java 9, there are two steps. The first step is just making it work on Java 9 without any modules of our own. Just running our regular class path, JAR ball of mud modification on top of the JVM, which is now modularized. That would actually be – that’s not what you asked about but that’s also challenging, and it’s more an all-or-nothing solution that you have to find there because you cannot just run half on Java 9.
But assuming you are past that, your application works on Java 9, now you are wondering, “But don’t I have to have modules? Don’t I have to fix all these problems?” No you don’t. You can just use the class path as before. And when you are in a situation – again, we assume that the application works on Java 8 on the class path, so it works on – even if you have version conflicts, even if you have missing dependencies, even if you have all that, apparently it works fine. And it will work find on the Java 9 class path as well.
It is when you start modularizing that – and you can do that step by step; you do not have to do it all at once – when you start modularizing it, then you will encounter situations where you realize “The module system does not support this.” And my personal opinion is most of the things that the module system doesn’t support are kind of things we got used to but we shouldn’t really be doing because it doesn’t sound like a good way to get a stable application. So, it just – it doesn’t mean that the application is bad and should never run. It just means maybe it’s not in the right place yet to be fully modularized.
And that’s fine too. You can just go – you can work on this over the next year or two or even longer, and at some point you have maybe 80 percent of the application modularized, but you have this small corner of it that just misbehaves and you never get it under control. Well, then, leave it for half a year or a year longer until it gets sorted out. So, if you encounter a situation where, like I said, I’m depending, basically I’m depending on this class path behavior, no problem, keep using that exact same behavior in Java 9 then.
Nate Black: What does it look like to use the module system? Does it change the way that people write code? Is it a change in tooling? What does it look like at the implementation level to use modules?
Nicolai Parlog: As I said earlier, a module is just a JAR with a module descriptor. It’s actually then we call it a modular JAR. It’s just a regular JAR. You can even compile for Java 8. It’s all fine. You can even run it on Java 8 then. You just have one additional file in there. It’s called a module-info.class.
It’s compiled from a module-info.java. And this is the module declaration. So, it’s a module declaration when it’s still .java and it’s a module declaration when it’s .class, but never mind if you mix it up. So, you have this module declaration, module-info.java, in your source tree. And this – if you have it – as soon as you have it there in the source tree and you compile, then the compiler sees, like, “A ha, we’re doing a module here.” So, it does all the module things and it expects the module things to be in place. Same at runtime. If you have a modular JAR – so, a JAR with that module descriptor – and you put it onto the module path, then the JVM is like, “A ha, we’re doing a module here, so I will want to have all these other modules in place.”
So, what I basically do as a developer, I only create this module for Java. What goes in there? It says “module” – that’s the first key word. But don’t worry, it’s just a key word in that file. So, if you have classes, you shouldn’t have classes called lower-m-modules. But if you have variables called lower-m-modules, don’t worry.
It’s only a key word in that context. So, you just go – in that file, you go “Module… your module name” – which has to be a regular Java identifier. So, the idea is, the recommendation is that your module is named as your packages are named. You use a domain that is associated with your project. Then you reverse it and you maybe have org.code of x.whatever, or com.your company.whatever. That would usually be the module name. Then curly braces, and then come two blocks: requires and exports.
Requires are what are the modules that I need? And you just list them. You say requires org library blah, and com framework blah. You just have a couple of requires clauses there. And then come the exports. We didn’t really talk about that yet. You export packages. You say which packages are part of my API. We can talk about accessibility later, but that’s – so, you put in the packages that you think “This is what I want to support.”
Specifically if you’re a library developer, “This is what I stand for. This is the stuff that people can use and that I will support, I will help them with.” And all of your packages that are called internal or other stuff, you really make – first of all, the module system guards these parts, but also make a statement like “This is not supporting API.” Beyond me calling the package internal, also you know tell the AVM, “Don’t let people use it.”
And then, there are a few advanced features, particularly services. They’re not advanced because they’re so much more complicated but because you don’t have to know them to start using the module system, that you put also in the descriptor down there – so, into the declaration. And then you’re done. You close the curly brace and you’re done. And basically, if I had a world, it’s like – or, if I had a word specifically, it’s just one line, but even for small DIM applications it’s maybe half a dozen lines. And with a real application it might get longer, but if you have one gigantic JAR and export – sorry, require two dozen libraries and export two dozen packages, then maybe the module doesn’t provide a lot of value anyway.
So, I think it will be like maybe two dozen lines tops for 80 percent of the modules that would be out there. And then you’re done. Then you’ve done all you needed to create your module.
Nate Black: To summarize, there’s no change to the Java code that I’m writing necessarily. There’s this extra small file called module-info.java that contains a list of all my dependencies as modules – so, the modules that I depend on – and then also my exported API as a list of packages that I’m exporting from this module. So, I’m explicitly saying what my supported API is.
Nicolai Parlog: Yes. After you condense your exports, your API, you condense it into that file. And then, if you think about it – and this is also a great way as having documentation as code. You can now – when you have – even when you’re not having a library, when you have a big application which has not like two JARs but maybe a dozen or two dozen or a couple hundred JARs even.
You have the problem that you create a JAR and you have a small subproject which has, I don’t know, like a dozen packages and you created two or three of them to be publicly used, and the other ones you didn’t consider them to be the API of the subproject you just wrote. With Java 9 you can put them into the module descriptor, you can even if you want add comments to explain the rationale and be like, “Okay, this is part of APIs, and these other packages, they might look nice but please don’t use them, blah blah blah. If you want to, open the issue over there.” So, you can help people – you can not only write down what is the API; you can also have one place where you can add information and your line of thought.
So, when we are in a big project and we are having a couple dozen modules and you wrote that module a couple of years ago and now I start using it and I – like, I want to use that package, before Java 9 I would just use that package. I would have no real incentive not to do it. I don’t have a real point in the development step where I’m like “Should I really use that package?”
I usually just do. Specifically, because my IDE just tells, like, “Do you want to auto-import that?” and I’m like “Sure, I want to auto-import that” – and done. Now, with – and my personal experience is that in code reviews nobody really looks at import clauses. There’s just so many of them that nobody really bothers going through them and “Which other JAR does this come from?” and “It wasn’t supposed to be done that way?” With a Word declaration you have one source file where this is condensed into, where you can – like, if I start using your old JAR, I have to add an export. And then, at code review somebody can say – or even during prep programming, though I think this is specifically a good thing to look at in code reviews, “Are you sure? Do you really want to have this new export there? Does it make sense for you?”
So, yes, that means that we’re going to think much more about public APIs.
Nate Black: Is this a change in thinking that’s on par with generics and lambdas in terms of the effect that it has on the way that people write code?
Nicolai Parlog: Will it change programming as the generics and lambdas did? I think it’s plain that no. In the day-to-day programming it will show much less. But that doesn’t mean it has less of a long-term effect. Many of the decisions that – or, many of the problems that eventually bring down or slow down projects are not that the classes got too wordy, which lambda fixes – right? – or – the problems, the long-term problems are often that development speed got so slow because everybody was just doing everything they – that was allowed to be done, and so you had a big ball of mud for every JAR reference, every other JAR, and you had no oversight over dependencies and over APIs. That’s where the term big ball of mud comes from, right?
And this, the module system is like a constant incentive or, let’s say – partially incentive but partially a tool that helps you not to go down that road, to keep in mind that maybe you don’t really want to do that.
Maybe you don’t want to add that dependency. Maybe you don’t want to add that export.
I’m talking about dependency, so I want to add another thing. Maybe we should go into readability and accessibility first. I’ll say it now and maybe I’ll say it again later. In the module system you cannot accidentally use your transitive dependencies anymore. So, what is very common is in a regular project that is separated, in a big project that is separated into little subprojects, I’m coding on X, and X uses Y, and Y uses Z. I can’t just use X because at compile time and in runtime Z is always present. But my main – my appointment doesn’t have to show up. So, I can actually be in a situation where I’m using Z but I never declare that I’m using Z. And the module system does not allow that, so I have to have – every direct dependency has to be listed in the module declaration.
Nate Black: Let’s bring that to a concrete example. Earlier, we talked about Spring having a dependency on OkHttp. Let’s say I have a direct dependency on Spring.
That means I have a transitive dependency on OkHttp. But declaring a dependency on Spring only means that I can’t also import things from OkHttp unless I also declare a dependency on OkHttp. I don’t get the transitive dependency.
Nicolai Parlog: Yes. Exactly. And that’s a good thing. It means that my modular descriptor for – modular declaration for my specific module will say Spring and OkHttp, and then I can have a discussion or a thought whether that’s – whether what I actually want to do that.
Nate Black: Let’s say I’ve specified my dependencies correctly. What does the module system do to resolve those dependencies and make it so that my program will build and launch?
Nicolai Parlog: When we think about dependency resolution we might think of what Maven does, like downloading and putting stuff on class path or in the future maybe a module path. It does put it on a module path if you’re creating a module. That part is not done by the JDK – by, the JVM, of course, of the compiler neither.
So, you have to have this part of the resolution where you’re like “Where are my artifacts and how do I drag them onto the corresponding paths?” That has to be done by some other tools – Maven, Gradle, whatever. So, there is no competition there at all. The JDM – sorry, the module system starts working at exactly the spot where the build will stop working, which is either during compilation or particularly at runtime.
Let’s talk about launch because launch is a clearer case. I’m launching my application, so I follow all my dependencies down to the module path. On Java 8 I would say, “This is the class path; these are all my JARs on the class path,” and then run this and that file. Or, maybe I say, “Run this and this class name or JAR.” And then, you run the main method from that class or from that JAR.
On Java 9, first of all on Java 9 you can do all the same things. You don’t have to have modules. If I have modules on Java 9, then I would put all of my code and all of my dependencies onto a module path, and assuming everything is modularized – in the mixed, modularized/non-modularized situation there are some more things that you have to do, but everything is modularized in this simple scenario.
I put everything onto the module path and I tell the JVM to launch that module. I don’t tell it to launch a JAR or a class; I would just tell it to launch a module. So, what happens then?
The module system looks into the module path. Is the module that you want to launch there? It’s there. Great. So, then what else? This module depends on some other modules. So, it looks: “Are there as well?” And then, it looks, “What do they depend on?” So, it builds this dependency tree in that case – it starts with a tree, but quickly it turns to back edges – so, let’s say it’s a dependency graph. It used to be called a module graph; I think the current nomenclature since the official Java 9 release, it’s called a readability graph. So, what the module system does, it checks whether your configuration that you provided it with is reliable. Is everything there? Do you not have ambivalences like duplicate modules or split packages? Is everything in order, so to speak? Does it look like you have a good chance to launch this application?
Not all things are checked. You can have an empty JAR claiming to be Guava and that will be fine.
You will only find out at runtime that actually, no, there’s no ImmutableList there. So, it doesn’t check all the things but it checks a lot of things. So, it provides you with a check whether your configuration is reliable, which I worded precisely to come up with – this is one of the major benefits, reliable configuration. This is a term you will hear a lot when it’s about the module system. You will get reliable configuration.
And also, we use another important term: readability. The module system builds this graph and whenever a module depends – or in this case uses requires clauses – right? – so, when a module requires another module and both of them are there when the JVM launches, then the first module reads the second module. That’s why it’s called the readability graph. So, in that graph that I started with earlier that we have in our minds, it’s also in the JVM now. Each module is a node, and the requires clauses between them turn into readability edges between these modules. So, you get this graph. This is the first part of the module system’s work: create that readability graph from the requires clauses that are spread through all of your code and dependencies and transitive dependencies.
Also, no statically declared circuits, by the way.
So, if you’re done with that and you start running the codes, it kicks in the second part of what the system does for you, which is strong encapsulation. I said it a couple of times already, that without the module system every public class can use every other public class. And I also said that if you have to export APIs… So, what exactly happens there?
Within a module access is as it was before. So, within a module you can use all other public classes and all other packages and all the classes in the same package that are protected or package-visible – so, nothing changes within the module. Across module boundaries, for my module to access ImmutableList in Guava there are three properties that have to be fulfilled of the requirements. The first one is ImmutableList has to be public, which is already the case now, so that’s nothing new. The second is the package containing ImmutableList must be exported by the module that contains it. So, that means – that makes the API public. If it is not, I get an “Inaccessible… something exception.”
So, it doesn’t work. In summary, it doesn’t work. So, it has to be public. It has to be in an exported package. And the third one is I actually have to read Guava, which I typically only do if I actually require Guava. That’s the thing that prevents me from accidentally using transitive dependencies. I can only access ImmutableList if I said I would require Guava – so, if I read Guava.
And these three requirements – public, export package, I have to read the module – these – all three have to be fulfilled in order for the module system to let me access the codes.
Nate Black: One of the benefits of using modules that you mentioned was encapsulation. How do we gain encapsulation by using modules?
Nicolai Parlog: Maybe we didn’t put the phrase on it, but we just discussed it. The accessibility rules, these three rules – must be public, must be exported, must be in a mode that I actually read – these three accessibility rules implement strong encapsulation, provide us with strong encapsulation.
Which means if everything is in a module, I cannot go past what the designer of a module thought would be his public API. First of all, I still can – if I have control of the command line, I can use specific flags to break into code so that there’s – always there’s a last resort. We usually call them escape hatches. So, if things break, you can actually massage a lot of the module system by using the command line.
But leaving that aside, it shifts power from the user of a library or a framework to the creators – not all of it, but parts of it – by giving the creator much more influence over which parts of the API is used. And generally I think that’s a really good thing because these others – using internals usually have two consequences. One of them is that I’m using code that is maybe less tested but surely less supported, so it might change maybe in two releases.
That means application developers that depend on internals are sure to get punished for that at some point in the future. And this is particularly worrisome because I’ve seen – when migrating an application to Java 9, I’ve seen quite a lot of these using JDK internal APIs, and I can tell you half of them I could just replace with supported APIs. It was really easy. There was no good reason to do that. It just took some time and it – there was no reason to do it in the first place. They could have just done it right in the first try.
So, not only do you use internal APIs when you really have that one percent special problem that can only be used that way, you also sometimes use internal APIs because it seems more apparent, or because – let’s be honest – it’s the first answer on the ____ flow.
So, with the JVM stepping in you cannot do that anymore. So, the first effect that you’re using – depending on internal APIs for application developers goes away. But then there’s a second aspect which is bad for the library developer.
So, the two examples I like to give are Unsafe and JUnit4. What do they have in common? Both of them – well, JUnit4 is of course a public and supported project but it has a lot of great integration with IDEs particularly. And IDEs started wanting to show the user more information that the API was provided with. And I don’t know why there was no extension to the API. Maybe it was discussed. Maybe it was just a quick fix to do it with Reflection. But what essentially happened was IDE developers used Reflection to break into JUnit4 internals and show information from the internal – from JUnit4 implementation details to provide users with information, to show them nice green check marks, I guess.
That was a problem because that meant that JUnit4 could not change things like some specific variable names or some class names or some package structure without breaking existing tools out there. And now, suddenly, it’s not only the application developer, or in this case the IDE developer, who is in a tough spot.
Now the project, which really didn’t do anything wrong, is in a place where they cannot freely refactor the code because somebody is using internals. And sure, they could be like, “Well, it’s your own damn fault. I didn’t do it anyway.” But you don’t want to break downstream tools and stuff, so they don’t. So, that was one of the reasons why JUnit4 was not seen in development anymore, and one of the two and a half reasons for making a total rewrite with JUnit5. So, let’s just guess that it was a 40 percent reason for a total rewrite. That’s a huge investment, like these people that have been working on JUnit5 for more than two years and put immense energy on that, 40 percent of that energy was because we didn’t accept what was internal and what wasn’t. And with “we” I don’t mean you and me; I don’t want to point fingers at any IDE developer. Everybody had their reasons. But the outcome was bad.
Similar things hold for Unsafe.
Sun.misc.Unsafe is a class that does a lot of stuff that is unsafe for Java developers, like direct memory access – I never learned C, so direct memory access sounds like something archaic to do. There is no reason to do that in regular Java programming, but if you are doing high performance Java libraries, for example, then there is a good reason to use that.
And so, this JDK class that is already called Unsafe and is already in a package that says sun.star – sun.anything packages, never use them. There was a note from Sun at the end of the ’90s already. Everybody knew they were not supposed to use it because of the name of the class and the name of the package. And the other hint was it was really complicated to get an instance because the constructor was private. So, you had to use Reflection to break into the class and make the aesthetic visible to get an instance of Unsafe. So, they did all the best they could to hide it. And still, when they said for Java 9 “Unsafe is going to be inaccessible,” all hell broke loose because many people realized how many of the tools were going to break.
So, to finish that story, the Unsafe story, it’s still accessible in Java 9 and will be phased out slowly. But the point is application developers might be in trouble with the dependency implementation details. But also, the people providing those implementation details, which we all have to do all the time, can get into problems when suddenly dependencies are so big or so important with these details that they don’t feel free to change them anymore. And this, then, is a severe hindrance to further development.
And what strong encapsulation does, it gives the developer more power, or the maintainer of a library of code more power to say what is going to be used and what is not going to be used, but also, I think, shifts the blame a little bit more because now it’s even clearer what we were supposed to be using and what we were not supposed to be using. And that’s the hope, that using internal APIs becomes much less common and thus phases out these two problems.
Nate Black: At first I thought that the module system only enforces compile time, that I don’t use things that aren’t part of an exported package, but you mentioned some examples –
where people tried to use Reflection to get instances of a class, and it sounds like the module system will also support at runtime that I don’t use Reflection to instantiate a class if it’s not supposed to be readable by me. Is that correct?
Nicolai Parlog: Yeah, so there are different ways to break into a library, right? One of them is to use regular loopholes, like for example I could – let’s say there’s a package-visible class in some Spring library. I want to use it. “Hey, maybe it’s not final.” I could just extend it with a public class that I put into that package over there – so, you could go like that, for example. Or then, if that doesn’t work, I can still use Reflection. So, there – let’s just say – let’s just call them compile time tricks and runtime tricks. And neither of them work. And all of the accessibility rules I just described, Reflection has no special powers and no superpowers anymore. It must adhere to all the same rules. So, yes, at compile time we get more checks.
But maybe more important even, at runtime as well for Reflection as well.
Nate Black: It may seem naively that adding modules or using the module system would add a lot of overhead for me and potentially slow me down. But you’re saying that there’s a real payoff to being able to develop code faster because encapsulation makes a smaller surface area that other people outside my module are touching. And as long as I don’t change things on that smaller surface area, internally I am free to make a lot of changes and can move that much faster without breaking people downstream.
Nicolai Parlog: Yeah, exactly. And that’s the perfect sales pitch. Exactly. Sometimes people ask, “But doesn’t that mean that everything gets more complicated in that case?” or they feel like that’s too much limitations. And my usual reply is “Unless you make every class or member public you are already kind of valuing encapsulation even within your own projects.” Right? So, this is just an extension of that idea.
It just means that I am taking the idea of making something that’s not public and project it onto the level of JARs where this didn’t exist so far.
So, yeah, I think what you just said is absolutely right. It gives us the freedom to declare surfaces that are smaller, particularly across JAR, and then gives us a strong incentive to adhere to that rule, to that decision that Maintainer made by making it harder to break into that API.
Nate Black: Do you think that people will want to migrate to Java 9 and modules, or do you think that there are things that will prevent them from migrating?
Nicolai Parlog: Going all the way from Java 8 to modules is by and large a two-step process. The first one I usually call migration, where you try to get your Java 8 stuff to work on Java 9. And the second one I usually call modularization, taking your class path-based code and turning it into modules. And the second part, the second step is very easy to take, be taking small steps. The first part isn’t, of course. If you want to run on Java 9, you have to run entirely on Java 9.
And there are a couple of things that can break your application moving to Java 9. Some of them are due to the module system. Some of them are just decisions that were made in the sense of “Hey, if we’re making problems anyway, maybe we can fix all of the things now and cause less problems in the future.” But most of these changes are not technically incompatible, because the vast majority were never standardized – especially since you can’t do it in a reasonable, with a reasonable effort – particularly after you’ve updated your dependencies. Do that first. Before you even think about Java 9, just update all your dependencies. That’s already sufficient work for most big applications. But you might find out – or, you might find out that you actually did have problems with Java 9 if you’re using Hibernate4, for example, or like an old version of Spring or something.
Most of the libraries that come out now support Java 9, and if you update to them, you might go past some problems that you had. You might fix them without even realizing it. And being up to date is a worth in itself, and also means that you might skip some Java 9 problems.
So, do that first before you even – you could do that first. That would be – a fair approach to Java 9 migration is “We’re just going to ignore Java 9 for the next couple of months. We’re just going to update everything.”
Nate Black: One thing we didn’t talk about yet is the new Java release schedule. Could you talk about that a little bit?
Nicolai Parlog: Yeah, true. We didn’t do that yet. That’s actually quite an awesome change. As I said earlier, most Java releases have an flexion feature. But the crux with that is waiting for that flexion feature to be done can delay features that are already ready. Why not ship them?
And so, I think it was a year back when the JDK team slowly asked around, “You know, how often could you stomach a Java release? What about fixed every two years? Or fixed every year? Or fixed every six months?” And the six month thing, to me it sounded like a crazy idea. I could not – that would be very – like, every six months? Really?
And immediately after Java 9 came out, or maybe even before, in the immediate vicinity around Java 9’s release, the proposal was made, “Yeah, let’s do that.
Let’s have a new Java release every six months.” And so, what sounded like a pipe dream a couple months back is now actually put in practice. Java 10 is going to come out in March 2018 and the plan is to have a fixed release schedule. Every six months Java gets released, whatever is in there.
So, the plan is to have, as I said earlier in this project, effectively feature branches. Conceptually, work is being done in those branches, and when the feature is done or almost done it gets merged into the mainline. And then, three months before a new release is made a release branch is cut from the main. So, they have three months of stabilizing a release and then it gets out.
So, we only know what goes into a new release three months before the release. In this concrete case, I think the deadline for Java 10 is December 14. So, whatever goes into Java 10 before – sorry, goes into the mainline before December 14 is going to be shipped with Java 10. And that means we get new features faster.
That means we get more – we have to be more aware of what happens with Java, because up to now it was fairly easy, actually, to – when Java 9 comes out in a couple of months you just start Googling for Java 9 because everybody writes blog posts or books or podcasts or whatever and puts Java 9 to the title. Now it’s not that easy anymore. If I want to tell you about what great feature might show up in the future, I cannot say it’s in Java 10 because Java 10 is in three months. It’s not in there. But I can guess it’s 11 or 12. So, I’ll just say, “This is Project Valhalla.” “This is Project Amber.” I talk about the project when I blog about it so – and other people do the same at conferences. You will only – you will talk about the projects that are behind that. And then, only a couple months before the release will people start being aware of “Oh, yeah, this feature got into Java 11.” So, that means that the community will have to change its way of learning about new Java releases.
And… that was the second thing. I think I had a third thing that I wanted to tell you about the new release. But hey, the third thing is I think it’s great if you don’t have to wait that long anymore for nice new things to play with.
Nate Black: On one hand we get a faster release cycle and more certainty about when the releases are going to happen, but for any particular feature it may take multiple releases and we won’t know until three months before the release which of those features got in.
Nicolai Parlog: Exactly. The Java – the JDK team will also work on trying to deliver features more incrementally. So, up to now, for example, lambdas and streams were both a big feature. They went hand in hand and they went into the same release. And maybe in the future – I think with those specific features it’s kind of it’s hard to cut them apart, but maybe in the future we will see a small part. For example, people are talking about pattern matching in Java 10. Whatever that exactly means and how it relates to Java is not that important, but there is a reasonable path to going from what Switch does not to what full pattern matching might do eventually. And there’s a small – there’s a step – sorry, there’s a path of small steps and we’re likely going to see these small steps being released individually.
One thing I forgot to mention is the long-term support. That’s of course always interesting. Java 9 is not a long-term support release.
So, now that we’re talking about having a release every six months, of course Oracle had to change its support policy because usually they used to say “Until the next release comes out plus a couple of months.” But that won’t work well anymore. So, what it’s done now is every sixth release – meaning every three years – a long-term support release comes out. The next one is Java 11. And you asked me about what could hinder people starting to use Java 9. They could look at this and say, “Wow, Java 9 – first of all, it’s a pain. But also, it’s not even long-term support. Let’s wait.” I wouldn’t recommend that. It doesn’t get better by waiting for more changes to come into the language.
But Java 9 is only supported until Java 10 comes out, and Java 10 is only supported until Java 11 comes out. And then, 11 comes out and it’s supported for the whole next three years, I guess plus X, plus a couple more months hopefully. So, you have a little bit of overlap between LTS releases. But I think that’s – I hope that we don’t end up in a situation where people only focus on the LTS releases. I really hope that people try to stay with the current versions.
Nate Black: So, your recommendation would be if you’re on Java 8, start working towards compatibility with Java 9 now. And in the worst case scenario you’ll catch up eventually when Java 11 is released.
Nicolai Parlog: Yes, absolutely. So, first of all, Java 9, the amount of changes in Java 9 specifically regarding compatibility is out of the ordinary for Java. And there is no expectation at all that the next three and a half years are going to see as many compatibility problematic changes as – I don’t want to say incompatibilities because they aren’t really hard incompatibilities, or many of them aren’t. But so, migration pain from Java 8 to Java 9 I think might maybe be the most of what Java has seen in the past, except Java 5 maybe, and will surely not be reached by any future release in the next couple of years. First of all, the total amount of change over the same amount of time, the same – the amount of innovation over the same amount of time stays constant. We’re not going to see more innovation and more changes just because we see a quicker release cycle.
Java 10 is – sorry, Java is committed to backwards compatibility as it was before. So, already now, for example, I’m experimenting with Java 10. It just works. No – IDEs, build tools, they don’t even know the difference. It just works. So, Java 9 will be a lot of work. Java 10, unless they do something very surprising, is no work at all. And I would guess Java 11 is very little work too. So, again, yeah, I would recommend doing the hard work earlier rather than later.
Nate Black: There is clearly a lot more that we haven’t covered, many more details and nuances to Java 9 that you blog about and that you wrote about in your book, The Java Module System. So, where can people go to find that information and find out more about you and the work that you do?
Nicolai Parlog: Yeah, so actually, I’m doing a little bit too much for my own good. So, I started blogging and you can always go to CodeFX.org – like, one word, CodeFX.org – and you will find my blog. Whatever else I do, you can always find from there.
The other thing you asked me about was the book, of course. So, I’m writing The Java Module System with Manning.
The principal writing will be done by the end of the year, and so I expect the release of the paper book early next year. But Manning has an early access program, and it’s already available as an e-book and already covers all the basics, all the migration, and I’m just starting now with advanced features, which I’ve already got a couple interesting in there as well.
So, you can go – either you can search for the thing “Java Module System” and “Manning,” or if you want a URL you go to tiny.cc/jms and you’ll be redirected to the Manning page. And if you use the code seradio.np – like, one word, seradio.np – if you use that code you get 40 percent off when you buy that book.
Yeah, I also started doing – I want to start a YouTube channel. I watch people talk on YouTube about programming and I love it. I want to do that as well. I don’t really have the time right now, but I already put up two videos, one about Java 9, one about the Java 10 feature. I invite you to check it out as well. It’s also CodeFX on the YouTube – the YouTube channel is also called CodeFX. Google for it or you’ll find a link on my home page.
And if everything else fails, follow me on Twitter where I’m at nipafx. And I promise you: no cat pictures. Mostly Java.
Nate Black: Okay. Great. There will be links to all of that in the show notes. Thanks again, Nicolai Parlog, for being on Software Engineering Radio.
Nicolai Parlog: Thank you very much, Nate. Thanks for having me.
Nate Black: This has been Nate Black. Thanks for listening.
Voice Over: Thanks for listening to SE Radio, an educational program brought to you by IEEE Software Magazine. For more about the podcast, including other episodes, visit our website at SE-Radio.net. To provide feedback you can comment on each episode on the website or reach us on LinkedIn, Facebook, Twitter, or through our Slack channel at SERadio.slack.com. You can also e-mail us at [email protected].
This and all other episodes of SE Radio is licensed under Creative Comments license 2.5. Thanks for listening.
[End of Audio]