I'd like to use `jshell` with JDK 8

8.9k views Asked by At

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.

2

There are 2 answers

4
Stephen C On BEST ANSWER

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.

  1. The Java 8 compiler will tell you that. So will an IDE, assuming you set the platform dependencies.

  2. 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 :-)

1
Naga Vijayapuram On

Here's what I do - I have both Java 8 and Java 17, I escape to Java 17 momentarily for the jshell session ...

jshell() {
  export JAVA_HOME=/opt/homebrew/Cellar/openjdk/17/libexec/openjdk.jdk/Contents/Home
  /usr/bin/jshell "$@"
  export JAVA_HOME=/Library/Java/JavaVirtualMachines/temurin-8.jdk/Contents/Home
}

I add the above bash function to my ~/.bash_profile

While this may not be the perfect way, at least I can try out few things that are common across Java 8 and Java 17 ...


~ java -version
openjdk version "1.8.0_302"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM (Temurin)(build 25.302-b08, mixed mode)

~ jshell
|  Welcome to JShell -- Version 17
|  For an introduction type: /help intro

jshell> String s = "abc"
s ==> "abc"

jshell> s
s ==> "abc"

jshell> s.charAt(1)
$4 ==> 'b'

jshell> /exit
|  Goodbye

~ java -version
openjdk version "1.8.0_302"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_302-b08)
OpenJDK 64-Bit Server VM (Temurin)(build 25.302-b08, mixed mode)