JShell, the Java REPL
![](/covers/misc/1QQn2VbwlEZDxtMYm53VdCQ.jpg)
Many languages contain a REPL, a Read-Evaluate-Print Loop. It evaluates declarations, statements, and expressions as they are entered and immediately shows the results. With Java 9, we finally got one too.
Quickly running some code without starting a full-blown IDE, or creating a scratch project can be immensely helpful. It can be used for prototyping code, trying some new functionality, or running a small part of a bigger project.
Table of Contents
Lines starting with #
are added for explanation purposes and are not part of the JShell output.
Basic Usage
To start a new JShell session, just type jshell
in your favorite terminal / command line:
jshell [options] [load-files]
Snippets
JShell evaluates everything you throw at it: expressions, statements, imports, or definitions. These chunks of Java code are called “snippets”.
# Expressions
jshell> 2 + 3
$1 ==> 5
# Statements
jshell> var a = 2 + 3
a ==> 5
# Imports
jshell> import java.time.*
# Definitions
jshell> boolean greaterZero(int i) {
...> return i > 0;
...> }
| created method greaterZero(int)
No semicolons are needed for single-line snippets. If we define a method or class, we still need semicolons for the contents of curly brackets.
Scratch Variables
Any value returned by a snippet is automatically saved into a “scratch variable”, named $<number>
. Now the result of the expression is available to use later on.
To list all variables of the active session, type /vars
, which will print all scratch and non-scratch variables:
jshell> 2 + 3
$1 ==> 5jshell> String.valueOf($1)
$2 ==> "5"
jshell> String nonScratch = "value"
nonScratch ==> "value"jshell> /vars
| int $1 = 5
| String $2 = "5"
| String nonScratch = "value"
Imports
By default, only a small subset of the JDK is imported automatically.
Typing /imports
lists all imported packages and classes of the current JShell session:
jshell> /imports
| import java.io.*
| import java.math.*
| import java.net.*
| import java.nio.file.*
| import java.util.*
| import java.util.concurrent.*
| import java.util.function.*
| import java.util.prefs.*
| import java.util.regex.*
| import java.util.stream.*
We can import other packages and classes just like we would in “normal” Java code:
jshell> import java.time.*
jshell> import static java.util.stream.Collectors.*
Or we can instruct JShell to load a different set of packages on start-up by using a predefined load-file: jshell JAVASE
This will load ~170 different packages from the JDK, instead of just the bare minimum.
Context-based Auto-Imports
No one wants to type out all the imports needed for working with Java code. It’s a good thing JShell has you covered!
To import a type we can press shift-tab i
at the end of any type, and JShell tries to find the correct type based on the context:
# cursor position
# ▼
jshell> var now = LocalDate.now() <shift-tab i>
0: Do nothing
1: import: java.time.LocalDate
Choice: 1
Not always an import-candidate will be found. But most of the time, it will make using JShell easier.
Expressions to Variables
Another handy shortcut is shift-tab v
which will convert an expression to a variable creation statement:
jshell> 3 + 3 <shift-tab v>
# New cursor position
# ▼
jshell> int = 3 + 3
The variable type will be inferred and inserted automatically, just give it a name, press enter
, and you converted your expression to a named variable.
The Power of Tab
The tab completion is really powerful and can be used to complete snippets and commands. Pressing tab
multiple times increases the amount of information provided.
jshell> System.out.print <tab>
print( printf( println(
jshell> System.out.print
If we found the correct method, we can see all available signatures by pressing tab
again after the parenthesis:
jshell> System.out.println(
Signatures:
void PrintStream.println()
void PrintStream.println(boolean x)
void PrintStream.println(char x)
void PrintStream.println(int x)
void PrintStream.println(long x)
void PrintStream.println(float x)
void PrintStream.println(double x)
void PrintStream.println(char[] x)
void PrintStream.println(String x)
void PrintStream.println(Object x)
<press tab again to see documentation>
jshell> System.out.println(
As indicated in the output, pressing tab
again (and again and again) will cycle through the documentation of every signature.
Methods & Classes
Methods and classes can be declared “as usual”:
jshell> String concat(String a, String b) {
...> return a + b;
...> }
| created method concat(String,String)
jshell> class SimplePojo {
...> private String value;
...> String getValue() {
...> return this.value;
...> }
...> void setValue(String newValue) {
...> this.value = newValue;
...> }
...> }
| created class SimplePojo
jshell> var pojo = new SimplePojo()
pojo ==> SimplePojo@67424e82
jshell> pojo.setValue("a value")
jshell> pojo.getValue()
$7 ==> "a value"
Forward Referencing
Thanks to forward referencing, our methods and classes can use variables and other constructs that aren’t even declared yet. JShell warns us about it, though:
jshell> void forwardReference() {
...> System.out.println(notYetDeclared);
...> }
| created method forwardReference(), however, it cannot be invoked until variable notYetDeclared is declared
jshell> forwardReference()
| attempted to call method forwardReference() which cannot be invoked until variable notYetDeclared is declared
jshell> var notYet = "variable is declared"
notYetDeclared ==> "variable is declared"
jshell> forwardReference()
variable is declared
This allows us to set up our environment easier than ensuring everything has to be in place before creating methods etc.
Control Flow
Java control flow statements are supported:
if
-else
? :
Ternary operatorfor
andwhile
-loopsswitch
statements
History
There are multiple ways to work with previous snippets.
Listing
The command /history
will display all snippets and commands of the currently active session in plain text.
jshell> 2 + 4
$1 ==> 6
jshell> String test = "42"
test ==> "42"
jshell> /history
2 + 4
String test = "42"
Using /list
instead, we get a list of snippets, but not commands, and their corresponding ID.
jshell> 2 + 4
$1 ==> 6
jshell> String test = "42"
test ==> "42"
jshell> /list
1 : 2 + 4
2 : String test = "42";
The ID can be used in other commands.
Searching
JShell supports backward and forward searching:
ctrl-r
- Backwardctrl-s
- Backward
Commands
We already learned about some available commands, like /vars
and /list
.
Many commands have different options. We can check them out by typing /?
or /help
for a short overview. For a detailed explanation of a command, use/? <command>
or /help <command>
.
List of Commands
Command | Description
--------------------|------------------------------------
/! | Rerun last snippet
/-<n> | Rerun the last <n>-th snippet
/<id> | Rerun specific snippet
|
/list | List source of session
/vars | List variables
/types | List types
/methods | List methods
/imports | List imports
|
/history | Plain text history
/drop <name or id> | Delete source entry
/edit | Open all source in editor
/edit <id> | Open specific source in editor
/open <file> | Open file as source input
/save <file> | Save snippets to <file>
|
/reload | Reset and replay each snippet
/reset | Reset JShell
/env | View or change the evaluation context
/set <key> <value> | Configure JShell
/exit | Quit JShell
Command Abbreviations
We don’t have to type out the full command, just enough to make it unique:
jshell> /set feedback verbose
VS.
jshell> /se fe v
/s
wouldn’t be enough for /set
, thanks to /save
.
External Code
Java provides a lot of functionality, but being able to use any JAR file in JShell makes it a potent tool for prototyping.
Classpath
We can either specify the classpath on startup:
jshell --class-path [directory or JAR files]
Or we can set it during an active session:
jshell> /env --class-path [directory or JAR files]
Beware, the session will be reset, and everything not in a start-up script will be gone.
Java 9 Modules
Module usage are similar to using classpaths:
jshell --module-path [module path] --add-modules [module]
This can also be set in an active session:
jshell> /env --module-path [module-path] --add-modules [module]
Configuration
With the help of /set
, we can configure JShell to our liking.
Basic Configuration
Command | Description
--------------------|------------------------------------
/! | Rerun last snippet
/-<n> | Rerun the last <n>-th snippet
/<id> | Rerun specific snippet
|
/list | List source of session
/vars | List variables
/types | List types
/methods | List methods
/imports | List imports
|
/history | Plain text history
/drop <name or id> | Delete source entry
/edit | Open all source in editor
/edit <id> | Open specific source in editor
/open <file> | Open file as source input
/save <file> | Save snippets to <file>
|
/reload | Reset and replay each snippet
/reset | Reset JShell
/env | View or change the evaluation context
/set <key> <value> | Configure JShell
/exit | Quit JShell
Like other commands, editor
has additional options, you can check them out with /help /set editor
.
Persistence
By default, all /set
commands are only set for the duration of the current session. To set a configuration option permanently, we need to add the argument -retain
at the end.
But be aware, not every retained option can be removed with -delete
like the editor option.
I’ve tried to find out where JShell saves its configuration, but couldn’t find it, even after checking out the source code.
Advanced Configuration
We can configure the output provided by JShell in quite detail. But this would make this article twice or thrice as long.
If you’re interested in configuring the output, you should check out the /set mode
and /set format
options.
JShell Feedback Mode Documentation (Oracle)
Working with Scripts
Start-up Scripts
As mentioned before, JShell loads a default start-up script to prepare the session with useful imports.
We can use our own script at startup to prepare it the way we like it:
jshell --startup [script]
To use more than one start-up script, we need to use the argument --start
instead:
jshell --start [script] --start [script]
Or we can use /set start -retain [scripts]
instead.
There are 3 built-in start-up scripts available:
DEFAULT
: Loads a minimum of imports, used if no load-file is specified.JAVASE
: Loads ~175 different JDK packages.PRINTING
: Loads the same imports asDEFAULT
, but also adds multipleprint
convenience methods.
Startup scripts are always run when the session resets:
- Initial start-up
/reset
/reload
/env
Loading Scripts
Loading a script into a session is also possible:
jshell [load-file]
Or use /open [load-file]
in a session.
Unlike a start-up script, it won’t be reloaded when the session resets.
Resources
- JShell User Guide (Oracle)
- JShell Tutorial (OpenJDK)
- JEP 222 (OpenJDK)
- Java 9 — Exploring the REPL (Baeldung)
- JShell Console in IntelliJ IDEA