IntelliJ IDEA
IntelliJ IDEA – the Leading Java and Kotlin IDE, by JetBrains
Real World Java 9
Last week we hosted a live webinar covering the features of Java 9 that are most interesting to developers. The recording is available here for those who missed it, and we also wanted to take this opportunity to answer all those questions we didn’t have time for during the session. If you read beyond the fold you’ll find the timeline of which features are covered (with links to that section in the video) and answers to all the questions (with links and more detail where relevant).
Timeline
- 1:25 Why Java 9?
- 3:15 The Case Study
- 4:17 Compiling with Java 9
- 9:07 Jigsaw (screen freezes here until 11:32, but it’s mostly talking anyway)
- 12:04 Java Platform Module System
- 29:50 Reactive Streams API
- 32:12 Questions (the answers are also covered below).
- All Other Features
- 43:39 Convenience Factory Methods for Collections
- 49:43 Private Methods on Interfaces
- 51:01 New Methods on the Streams API
- 54:17 New Methods on Optional
- 58:52 The Stack Walking API
- 59:47 Process API Updates
- 1:01:58 Multi Release JAR Files
- 1:05:19 Updated Deprecation
- 1:06:43 HTML 5 Javadoc & Javadoc Search
- 1:07:47 JShell & Support in IntelliJ IDEA
- 1:09:33 Summary
Questions
Is there Maven support for modules? Using the standard Maven folder structure? Eg: src/main/java/…/
Yes, you can use the standard Maven/Gradle file structure with Java 9 modules. All you need is to make sure you put your module-info.java file into the "java" folder, i.e. the root folder your packages start from.
Maven has full support for Java 9, although not all plugins will work. See also Older projects with module-info. Now Gradle also works with Java 9 (since Gradle 4.1).
Can you use the new module system for creating android applications (in the future)?
I don’t know the answer to that question, nor does there appear to be an official answer from Google. However, since Android now supports a subset of Java 8 features, one can assume that even with the addition of official support for Kotlin on Android, the support for Java will continue to evolve to include language features from Java 9 and upwards. The switch to using the OpenJDK for Android should help this, and there is a JDK 9 Android port.
Do you plan to clarify situation about using libraries without module-info? For example about using google adwords API.
You can use libraries that are not Java 9 modules as if they were modules. Java 9 supports all non-modular Jar files as Automatic Modules. These have special rules to make them behave like they used to before Java 9, but you should consider that the names of these modules and their behaviour may change if or when they are ever migrated to true Java 9 modules. It’s worth reading more about the whole module system to understand this a bit better, but for now it may be enough to know that you can still depend upon external libraries that are not modules even if you switch to using the Java 9 module system.
How we can find all unsupported packages?
I assume what you want to do is find if any bits of your code are going to break before you migrate to Java 9. There’s a tool called jdeps which is designed to do this, and you can run it from Java 8 so you don’t even need to download Java 9 before checking your code. It has a bunch of options and can be a bit tricky to run, but basically you want to run it with the -jdkinternals flag set. I wrote about how to do this in this tutorial (on page 19).
The module needs to be named the same as the package name?
No. A module can contain more than one package, so it doesn’t have to have a one-to-one relationship with package name. However, it’s strongly recommended that module names follow reverse-DNS conventions (i.e. com.jetbrains.someproduct):
Module names should be reverse-DNS, and so automatic modules can be given stable names: https://t.co/llq0uSwUPw #java #jigsaw #jdk9 #openjdk
— Mark Reinhold (@mreinhold) May 4, 2017
Therefore you’ll see in my examples my modules are called things like com.mechanitis.demo.sense.client – where com.mechanitis.demo.sense is effectively the application, and client is the module. My client module has more than one package inside:
Module names don’t need to follow this convention, but it is a reasonable suggestion in order to make it easier to select unique and stable module names.
In Java 9, to take advantage of Jigsaw, is it recommended/required that packages are contained in a single module?
A single package can only appear in a single module. Therefore if I have a package com.mechanitis.demo.sense.mood, this can only appear in one module (in my case, the mood module) and I can’t have a package with the same name in any other module. If I have this package in more than one module, it’s called a split package and isn’t allowed in the module system (video – the discussion of split packages goes on for about eight minutes). In fact the tool jdeps we talked about earlier will warn you if the libraries you depend on right now have this problem.
Is it correct that with modules I have to type the package names twice: once as module’s require part, the other as import’s part?
Not quite true but nearly. The module-info.java "requires" directive needs a module name, not a package. It might look like a package name, and might even match a package name, but it’s important to understand that you require a module, and when you do you get access to all the packages that have been exported from that module.
So if I have:
requires com.mechanitis.demo.sense.client
in the module-info.java file of my module, and the module-info.java file of the client module looks something like:
module com.mechanitis.demo.sense.client { exports com.mechanitis.demo.sense.client; exports com.mechanitis.demo.sense.client.mood; exports com.mechanitis.demo.sense.client.user; }
I can use anything from the client, client.mood and client.user packages in my own classes. And then yes, I will have to also import those packages in my class like I always did, for example:
import com.mechanitis.demo.sense.client.Dashboard; import com.mechanitis.demo.sense.client.user.LeaderboardData; import com.mechanitis.demo.sense.client.mood.MoodChartData; public class MyClass { }
Note that I don’t have to add "requires com.mechanitis.demo.sense.client" and "requires com.mechanitis.demo.sense.client.mood" and "requires com.mechanitis.demo.sense.client.user", because I don’t require packages, I require modules. Just "requires com.mechanitis.demo.sense.client" gives me access to all three exported packages in that module.
Of course if you’re using IntelliJ IDEA, you’re not "typing" anything twice, you’ll use code completion and quick fixes for these things.
If there were two packages: java.lang.logging, java.util.logging. How would I know which one I require by require java.logging in module-info.java?
Well, the short answer to this is that you don’t need to know, IntelliJ IDEA will fix this up for you. If your code is using java.util.logging.Logger, IntelliJ IDEA sees that this package is exported in the java.logging module:
and will therefore suggest you add java.logging to your module-info.java:
But I think what’s important to understand is that a package can only belong to one module, and that when you "require" that module you get access to all its exported packages. The question is unclear about where those two packages are, whether they are in the same module or not, but in actual fact it doesn’t matter – if there were two packages, java.lang.logging and java.util.logging (for full clarification, java.lang.logging doesn’t exist but let’s assume it did for a minute) and they were both exported from the java.logging module with:
module java.logging { exports java.util.logging; exports java.lang.logging; }
Then when your module has "requires java.logging" you get access to both these packages, and you just import the Logger that you want as usual. You either use
import java.util.logging.Logger;
or
import java.lang.logging.Logger;
From a Java class file point of view, all of this is exactly the same as it ever was.
If the two packages were in two different modules, then to use either one you have to identify which module the package is in. We saw earlier that IntelliJ IDEA will suggest you require java.logging if you try to
import java.util.logging.Logger;
and similarly if you want to import java.lang.logging.Logger, it would suggest adding the module that contains that package to module-info.java (provided that module was either in the JDK, e.g. is something like java.fictionallogger, or is on the classpath as an external JAR file).
For external dependencies, are those put into the project by Maven or some other package manager when added to the module-info.java?
Dependency management is completely independent of the module system. It looks like you’re declaring dependencies when you state which modules you require, but the JPMS is not a system for defining and fetching dependencies. All you’re doing is telling the JVM which modules you’re going to use in advance, not what they look like as jar files or where to get them from. You should still be using Maven, Gradle, or whatever dependency management you’re using right now as well as the new module system.
Similarly, JPMS specifically also doesn’t care about versioning, again this is a problem for the dependency management tool. The key thing to understand is that JPMS modules are not artifacts.
Can we have many Java 9 modules in single IntelliJ sub-project (sub-module?), or every Java 9 module should be in separate InteliJ sub-project (module)?
Every Java 9 module should map to a single IntelliJ IDEA module, there should be a one-to-one relationship between your JPMS (Java 9) module and your IntelliJ IDEA module.
Can Jigsaw point to an external repository and pull down dependencies? Like Maven central for example.
No, Jigsaw is not intended to replace the dependency management tools we’re using right now.
Do I have to take any special measures to make my app working with Dependency Injection Frameworks? They usually relied on reflection.
If you are using libraries that use reflection, you may need to make some changes if you move to Java 9, although in theory everything should work as before "out of the box" if you don’t move your application to use the new module system. You may have to set some command line options, you might find this article useful. I just tested my original Java 8 application running with Java 9, and it all works without me having to make any changes, and my JavaFX UI relies on reflection.
If you do adopt the module system, you simply have to "open" the relevant packages to the modules that need access, like I did in my client module to allow JavaFX to use reflection on my code:
module com.mechanitis.demo.sense.client { exports com.mechanitis.demo.sense.client to javafx.graphics; opens com.mechanitis.demo.sense.client to javafx.fxml; opens com.mechanitis.demo.sense.client.mood to javafx.fxml; opens com.mechanitis.demo.sense.client.user to javafx.fxml, javafx.base; }
Do you think module versioning will be introduced in future updates?
It’s possible, the JDK team have said they will continue to improve the module system over the next few versions of Java.
What’s the main benefit to use Java 9 modules over Maven/Gradle?
You should still be using Maven/Gradle if you’re using them now, so it’s not an either/or. You’ll use Maven/Gradle AND Java 9 modules.
Can it be used with Spring? In what version?
Older version of Spring may work with Java 9, and the latest version 5.0 has specific support for JDK 9.
How does Jigsaw impact Kotlin?
We had to do some work on the tooling and fix a number of library issues, like every other project on this planet! But 1.2 supports all of Jigsaw, and 1.1.50 has most of it already
Is there a way to use jlink with a Maven build?
Apparently there’s a jlink plugin, although it’s still at pre-release stage. It would be worth trying this out and giving any feedback you have to the team that’s working on it.
If the file is not public in the module definition, but your implementation does need it, how would you get that file?
You can’t, that’s the point.
However. If you’re talking about accessing internal classes in the JDK that you’ll need while you migrate your code to a suitable replacement, you can potentially use one of the "permit illegal access" flags.
What about automodules with different versions if they used in your different modules?
If you have more than one version of the same library (e.g. two jar files with different versions, like ext-library-0.1.jar and ext-library-0.2.jar) on your application classpath at the moment, this kinda works but is never really a good idea. If you make your application modular (i.e. start breaking your application into modules with module-info.java files and start using module path instead of classpath) and have dependencies on these same jar files, you’ll get an error. You’ll need to select just one version to put on your module path, and your automatic module will refer to that version.
Remember, there’s no versioning support so if you have ext-library-ver-0.1.jar and ext-library-ver-0.2.jar on your module path, both of them map to a module name of ext.library and the module system has no way of knowing which one you mean, so you can only have one version on the module path.
Can Java 9 modules be used in Java 8 code like they where just regular external jars?
Since Java 8 has no concept of modules, there’s no way to use modules in Java 8 – you can’t add a module-info.java file (it won’t compile) and there’s no other way to define which modules you want to use.
If what you mean is, can you use a JAR file that’s a Java 9 modular jar as a dependency in a Java 8 project, then yes, you can do that (I believe – I haven’t tried it). Java is always designed to be backwards compatible, and the JAR file structure for basic classes etc remains the same, it’s just if it’s modular there’s some additional info in there that Java 8 can ignore.
How can we get a list of modules which are exported by some library?
A single JAR file is a single module. You can see which packages are in that module by looking at its module-info.java file.
Can a module export a specific class instead of an entire package?
No, modules export packages.
If two module definitions use the same name, is there a way to import them under a synonym so as to not cause conflicts?
No. Basically two modules can’t have the same name. This should be fine for "real" Java 9 modules but could potentially be a problem in the early days of migration when automatic module names are determined by the name of the JAR file. One of the reasons to add a Automatic-Module-Name to the MANIFEST.MF file of a JAR even if it’s not using Java 9 modules (yet) is to encourage sensible unique names.
For Collection factories: Are there IDEA-hints and quickfixes for many of the use cases?
Yes, for List.of and Set.of. Not for Map, at the moment.
The List.of / Map.of … are those unmodifiable lists in the sense of vavr ? Can add elements to the list/map and get a new instance, that contains the old map + the new element?
No, these are AbstractImmutableList implementations (at the moment, anyway), which do not allow you to append to them or to alter them. You don’t get the feature that you get from some immutable collections of "changing" the list but actually getting a new instance with the new values. This isn’t a pattern that’s seen much in JDK classes.
Phew. What a lot of really great questions! I hope some of those answers help. Jigsaw and the JPMS is a massive topic that can’t easily be covered by 30 minutes of webinar and a blog post of FAQs, so I recommend a) checking out some of the articles I link to in my Real World Java 9 Resources page and b) consider reading one of the many books on the topic, it’s big and complicated enough to deserve further reading.