I tried out the JDK 11 jshell
recently and liked being able to interactively test out what's documented in core Java. My company is still tied to releasing with Java 8 though. So I thought I'd try and see if I can use the jshell
to work with JDK 8.
Edit to try and explain what seems unclear: I want to use JShell, but use the Java 8 APIs for interactive mode, scripts, completion, JavaDoc etc.
My mental model of what the jshell
was doing is: It is built with the JDK it was provided with, it therefore runs on the JRE it was built for, but I thought it loaded the JavaDoc, Auto-completions, from a JDK collection of jars, and ran the interactive scripts against another instance of the JVM, not necessarily the one with the same version as it is running on. So I thought I could control that second part with the JAVA_HOME
environment variable.
On a Mac, my simple first attempt was to:
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/ \
/Library/Java/JavaVirtualMachines/jdk-11.0.1.jdk/Contents/Home/bin/jshell
But it's not obvious to me that this is working at all (I'll wrap some lines):
jshell> System.getProperties()
$3 ==> {gopherProxySet=false, awt.toolkit=sun.lwawt.macosx.LWCToolkit,
java.specification.version=11, sun.cpu.isalist=, sun.jnu.encoding=UTF-8,
java.class.path=., java.vm.vendor=Oracle Corporation, sun.arch.data.model=64,
java.vendor.url=http://java.oracle.com/, user.timezone=, os.name=Mac OS X,
java.vm.specification.version=11, sun.java.launcher=SUN_STANDARD, user.country=US,
sun.boot.library.path=/Library/Java/JavaVirtualMachines/jdk-11.0.1.jdk/Contents/Home/lib,
sun.java.command=jdk.jshell.execution.RemoteExecutionControl 55110,
jdk.debug=release, sun.cpu.endian=little, user.home=/Users/lamblin,
user.language=en, java.specification.vendor=Oracle Corporation,
...
patch.level=unknown,
java.library.path=/Users/lamblin/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.,
java.vendor=Oracle Corporation, java.vm.info=mixed mode,
java.vm.version=11.0.1+13-LTS, sun.io.unicode.encoding=UnicodeBig,
java.class.version=55.0}
jshell> System.getenv("JAVA_HOME")
$4 ==> "/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/"
jshell> exit
Why? well the Stream.ofNullable(T)
is new in Java 9 and while I'd like it to tell me that it's not in Java 8, it instead works pretty normally.
jshell> Stream.ofNullable(null).iterator().hasNext()
$3 ==> false
jshell> Stream.ofNullable("hi").iterator().hasNext()
$4 ==> true
jshell> Stream.ofNullable(
Signatures:
Stream<T> Stream<T>.<T>ofNullable(T t)
<press tab again to see documentation>
jshell> Stream.ofNullable(
Stream<T> Stream<T>.<T>ofNullable(T t)
Returns a sequential Stream containing a single element, if non-null, otherwise returns an
empty Stream .
Type Parameters:
T - the type of stream elements
Parameters:
t - the single element
Returns:
a stream with a single element if the specified element is non-null, otherwise an empty stream
<press tab again to see all possible completions; total possible completions: 544>
jshell> Stream.ofNullable(
Perhaps what I want isn't possible with this jshell
.
I had started thinking it was when I saw a video using jshell
in NetBeans on JDK 8. This video may have been misstating what was actually accomplished.
I did find that changing the class-path isn't what I'm after because that stops the jshell
from being able to load classes it needs.
So not: /Library/Java/JavaVirtualMachines/jdk-11.0.1.jdk/Contents/Home/bin/jshell --class-path /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/*
It is possible to start the jshell
normally and then add one of the JRE 8 jars to the class path with /env -class-path /Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/jre/lib/rt.jar
. This can be done with with all the jars in jre/lib
but it won't unload or replace what was loaded, so it doesn't end up giving you an environment limited to running Java 8 snippets.
This is not using using jshell
in Java 8.
JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home/ \
/Library/Java/JavaVirtualMachines/jdk-11.0.1.jdk/Contents/Home/bin/jshell
Why? Because JDK tools don't actually use $JAVA_HOME to decide which JVM to use. Instead the decision is made by the executable (or wrapper script) based on its file location.
But you can confirm this by running jshell -version
. (Note: only one -
!!)
If you download the OpenJDK source, you can see that jshell
is mostly implemented in Java. And it has a main method. So you might be able to compile the jshell
classes with a Java 8 compiler. But that is likely to fail1, because jshell
knows about modules, and that means it has dependencies on module-related API changes that were made in Java 9.
But ... why bother?
Why? well the
Stream.ofNullable(T)
is new in Java 9 and while I'd like it to tell me that it's not in Java 8, it instead works pretty normally.
The Java 8 compiler will tell you that. So will an IDE, assuming you set the platform dependencies.
You might be able to use the -C
option to get jshell
to compile your code against the Java 8 APIs.
1 - If you put in the effort to work around these anticipated dependencies, and other Java 9+ dependencies in the jshell classes, then you will have succeeded in backporting it to Java 8. Fame and fortune awaits you :-)