In this post I provide a tutorial on using awesome jshell
tool.
1. Motivation
There are many times, when you just want to try out some code snippet in Java. For example, you want to experiment with new constructs available in recent Java versions, something like var
.
For a long time, to do this you had to follow Write-Compile-Execute Loop
:
write the Java program (of course with
public static void main(String… args)
)compile written code with
javac
(and potentially fix compile-time errors)execute compiled byte-code with
java
(and potentially fix run-time exceptions)edit written program and repeat the process
The frustrating thing here is delay between the time you start writing code and the time you actually got the feedback from Java compiler/runtime. However, now we have a tool, that finally removes unnecessary hustle. Please, meet jshell
!
jshell
significantly shortens the feedback loop — thus increasing your productivity. The boost is achieved by eliminating the need to switch back and forth between your editor/IDE and shell, since you write Java code in the dedicated shell (hence the name, jshell
). More precisely, jshell
implements Read-Evaluate-Print Loop
:
reads the code from command line
evaluates the given snippet
prints the result back to you
If you think about it — this is pretty awesome!
Hopefully at this point you’re eager to play with jshell
, so, without further ado — let’s start.
2. Starting / exiting jshell
Using JDK 9+
jshell
is available on JDK 9+ and above. So, if you have one of the recent Java versions available in the $PATH
, then all you need to do is simply launch jshell
:
$ jshell -v
| Welcome to JShell -- Version 11.0.4
| For an introduction type: /help intro
jshell> /exit
| Goodbye
-v flag — enables verbose feedback mode, that gives you a log of comments from jshell . |
Using Docker
If you want to experiment with most recent features of Java in clean and safe playground — then just use Docker:
$ docker run --rm -it adoptopenjdk/openjdk13 jshell -v
Nov 03, 2019 12:08:49 AM java.util.prefs.FileSystemPreferences$1 run
INFO: Created user preferences directory.
| Welcome to JShell -- Version 13
| For an introduction type: /help intro
jshell> /exit
| Goodbye
/exit — quites jshell . |
3. Snippets
As already mentioned, the jshell
tool allows you to execute Java code, getting immediate results. You can enter:
Java definition (variable, method, class, etc), like:
int x = 8
Java expression, like:
x + x
Java statement or import, like:
import java.time.*
These little chunks of Java code are called snippets
.
Let’s try write a few snippets to get familiar:
jshell> 1 + 1
$1 ==> 2
| created scratch variable $1 : int
jshell> int n = 1 + 1
n ==> 2
| created variable n : int
jshell> String hello(String g) {
...> return "hello " + g;
...> }
| created method hello(String)
jshell> String hello(String name) {
...> return "Hi " + name + "!";
...> }
| modified method hello(String)
| update overwrote method hello(String) (1)
jshell> hello("Alina")
$5 ==> "Hi Alina!"
| created scratch variable $5 : String
jshell> String n
n ==> null
| replaced variable n : String
| update overwrote variable n : int (2)
jshell> double volume(double radius) {
...> return 4.0 / 3.0 * PI * cube(radius); (3)
...> }
| created method volume(double), however, it cannot be invoked until variable PI, and method cube(double) are declared
jshell> double PI = 3.14159
jshell> volume(1)
| attempted to call method volume(double) which cannot be invoked until method cube(double) is declared
jshell> double cube(double a) { return a * a * a; }
jshell> volume(1)
$11 ==> 4.188786666666666
jshell> int divide(int a, int b) {
...> return a / b;
...> }
jshell> divide(1, 0)
| Exception java.lang.ArithmeticException: / by zero
| at divide (#12:2) (4)
| at (#13:1)
jshell> /list (5)
1 : 1 + 1
4 : String hello(String name) {
return "Hi " + name + "!";
}
5 : hello("Alina")
6 : String n;
7 : double volume(double radius) {
return 4.0 / 3.0 * PI * cube(radius);
}
8 : double PI = 3.14159;
9 : volume(1)
10 : double cube(double a) { return a * a * a; }
11 : volume(1)
12 : int divide(int a, int b) {
return a / b;
}
13 : divide(1, 0)
1 | overwrite existing variable/method/class definition |
2 | overwrite existing variable/method/class definition using incompatible type |
3 | forward reference variables/methods/classes, that are not yet defined |
4 | #id:line-number snippet id and line number withing a snippet, that caused an exception |
5 | /list command to trace back the source of exception |
Verbose mode gives a lot of commentary, that might be useful, when you just start learning about
|
jshell
supports auto-completion with <Tab>
key:
jshell> "hello". [Tab] (1)
charAt( chars() codePointAt(
codePointBefore( codePointCount( codePoints()
compareTo( compareToIgnoreCase( concat(
contains( contentEquals( describeConstable()
endsWith( equals( equalsIgnoreCase(
formatted( getBytes( getChars(
getClass() hashCode() indent(
indexOf( intern() isBlank()
isEmpty() lastIndexOf( length()
lines() matches( notify()
notifyAll() offsetByCodePoints( regionMatches(
repeat( replace( replaceAll(
replaceFirst( resolveConstantDesc( split(
startsWith( strip() stripIndent()
stripLeading() stripTrailing() subSequence(
substring( toCharArray() toLowerCase(
toString() toUpperCase( transform(
translateEscapes() trim() wait(
jshell> "hello".startsWith( [Tab+Tab] (2)
jshell> "hello".startsWith("h") [Shift+Tab v] (3)
jshell> boolean _ = "hello".startsWith("h") (4)
1 | press Tab key to auto-complete |
2 | press Tab two times to show text documentation |
3 | press Shift+Tab+v to declare the variable with already entered value |
4 | after pressing Shift+Tab+v, the cursor is placed in the line, where you need to enter variable name |
Shift+Tab then v — the expression will be converted to a variable declaration Shift+Tab then m — the expression or statement will be converted to a method declaration Shift+Tab then i — propose possible imports |
4. Commands
jshell
commands control the environment and display information.
Command are distinguished from snippets by a leading forward slash /
.
Probably, the most useful is /help
command — it shows a list of available commands. Also, it allows to dig dipper into specific command. For example:
jshell> /help /var
|
| /vars
| =====
|
| List the type, name, and value of variables that were entered.
|
| /vars
| List the type, name, and value of the current active variables
|
| /vars <name>
| List variables with the specified name (preference for active variables)
|
| /vars <id>
| List the variable with the specified snippet ID.
| One or more IDs or ID ranges may used, see '/help id'
|
| /vars -start
| List the variables in the evaluated startup snippets
|
| /vars -all
| List all variables including failed, overwritten, dropped, and startup
jshell
has a default startup script that is silently and automatically executed before start, so that you can get to work quickly. Entries from the startup script aren’t listed unless you request them with the /list -start
or /list -all
command:
jshell> /list -all
s1 : import java.io.*;
s2 : import java.math.*;
s3 : import java.net.*;
s4 : import java.nio.file.*;
s5 : import java.util.*;
s6 : import java.util.concurrent.*;
s7 : import java.util.function.*;
s8 : import java.util.prefs.*;
s9 : import java.util.regex.*;
s10 : import java.util.stream.*;
The default startup script consists of several common imports.
|
Similar to snippet completion, when you enter commands and command options, use the Tab key to automatically complete the command or option.
jshell> /
/! /? /drop /edit /env /exit
/help /history /imports /list /methods /open
/reload /reset /save /set /types /vars
<press tab again to see synopsis>
Yet another useful way to get help, is to press Tab second time, to see synopsis for specific command:
|
Using Tab in a file argument position of the command shows the available files:
jshell> /open
.dockerenv / bin/ boot/ dev/ etc/
home/ lib/ lib64/ media/ mnt/ opt/
proc/ root/ run/ sbin/ srv/ sys/
tmp/ usr/ var/
<press tab again to see synopsis>
Command (and command option) abbreviations are supported, as long as the abbreviation is unique.
For example, the only command that begins with /l
is /list
, and the only /list
option that begins with -a
is -all
. Therefore, you can use the following abbreviations to enter the /list -all
command:
jshell> /l -a
5. Search
By pressing up arrow / down arrow you can navigate through the history of entered items.
However, sometimes it’s pretty tedious to go through history one line at a time. So, to perform backward history search — use Ctrl+R:
jshell>
bck-i-search: _
Now, you can enter keyword to search for (e.g. class
— if you want to search for all previously defined classes). You can continue go further back history by repeatedly pressing Ctrl+R. And if at some point you want to move the search forward towards the present — use Ctrl+S.
6. External editor
An alternative to editing at the command prompt is to use an external editor. This editor can be used to edit and create snippets, and is especially helpful for multiline snippets.
To edit a specific snippet in an editor, use /edit
command with snippet name or id. Use /list
command to get snippet id.
You can configure jshell
to use the editor of your choice with the /set editor
command:
jshell> /set editor vim
| Editor set to: vim
jshell> /edit
Now, you can define multi-line snippet in external editor, and after saving and closing editor, jshell
prompt is restored:
jshell>
If you don’t specify an editor, then the following environment variables are checked in order: JSHELLEDITOR, VISUAL, and EDITOR. If none of those are set, then a simple default editor is used. |
7. Conclusion
jshell
doesn’t replace IDEs. The tool is there for you to learn and experiment with Java code.
jshell
provides very convenient and safe playground. So, you can try different ideas and, after getting satisfactory results, just copy final code into your program editor or IDE.
Overall, in my opinion, jshell
significantly shortens the feedback loop — thus increasing your productivity. The boost is achieved by removing the need to switching back and forth between your editor/IDE and shell.