Introducing JShell 12, the rich REPL implementation

Whenever I read the release notices of a new JDK, I find interesting features of the Java language that I may not have noticed before, and I usually I come across a bunch of new APIs and methods that I would like to try. For example, type inference for local variables var, built to allow me to use a special reserved type name var, instead of an actual type name, like this:

var name = “Mohamed Taman”;

There are so many new things to try in the Java language all the time — new APIs (like the HTTP Client API), methods (like the String class methods), and expressions (like the switch expression). I can’t wait to jump into the examples!

But actually, you should wait before jumping into the code. You will need to use an IDE to experiment with the new features and there are many you can choose from that support Java SE 11 or 12, such as Apache NetBeans 10, IntelliJ IDEA 2018, or the new Eclipse.

Personally, I prefer to use an interactive programming environment that will allow me to quickly learn the new language features, explore the new APIs, and even prototype some complex code of my own without having to engage in the tedious cycle of editing, compiling, and executing code. You’re familiar with the process:

  1. Write a complete program
  2. Compile it and fix any errors
  3. Run the program
  4. Figure out what is wrong with it
  5. Edit it
  6. Repeat the process

The process is brilliant when you’re crafting the actual prototype or production code, but it’s a bit cumbersome for learning how to use new version features.

My suggestion is that you explore the JShell tool that is built-in and shipped with every JDK since Java SE 9.

JShell in a nutshell

Java 9 introduced JShell, a shiny, fancy tool, so now Java has a rich REPL (read-eval-print loop) implementation packaged as an interactive programming environment.

What’s a REPL?
A REPL (a read–eval–print loop), which may also be called an interactive top-level language shell, is well-explained by its name. The simple, interactive programming environment takes single user inputs, evaluates them, and returns the results to the user. Common examples include some scripting languages and of course, command-line shells.

How does JShell make magic? It provides a fast and friendly environment that enables you to quickly explore, discover, and experiment with Java language features and the extensive libraries. With JShell, you can enter program elements one at a time, immediately see the results, and make adjustments as needed.

In other words, JShell replaces the tedious cycle of editing, compiling, and executing viaits read-eval-print loop. Rather than complete programs, you write JShell commands and Java code snippets.

When you enter a snippet, JShell immediately reads it, evaluates it, and prints the results so you can see the effects of your code. Then it loops to perform this process again for the next snippet. JShell provides you with instant feedback that can enhance your coding performance and speed the learning and software development processes.

Let’s go through some quick examples to understand how to interact and use this Java REPL features of JShell.

To get started, you will need to know

The three items you will need to start this learning journey are the right tools, the knowledge of how to start a JShell session, and how to execute statements. The section on executing statements contains a lot of information.

The correct software

To work with JShell, basically just the JDK is required since you probably have Java SE or JDK 11+ installed. If you don’t, install Java SE or JDK 11+, and make sure the tools in the JDK bin folder are configured to be accessible from anywhere in your system. You can also install the JDK 12. (At the time of this writing, I was using Java SE JDK 12 early access.)

How to start a JShell session

To start interacting with the tool, start a JShell session like this:

  1. In Microsoft Windows, open a Command Prompt then type jshell and press Enter.
  2. In Linux, open a shell window then type jshell and press Enter.
  3. I’m on MacOS (formerly OS X), so I’ll open a terminal window and type jshell and press Enter.

This command executes a new JShell session and displays this message at the jshell> prompt:

mohamed_taman:~$ jshell--enable-preview
|  Welcome to JShell -- Version 12-ea
|  For an introduction type: /help intro

jshell>

Version 12-ea in the first line indicates that I’m using Java SE JDK 12 early access. JShell precedes informational messages with vertical bars (|). You are now ready to enter Java code or JShell commands and snippets.

The --enable-preview option allows you to unlock the new language features that are not part of the JDK yet; they are disabled by default and are in experimental stages. One of these new features is the switch expression that I mentioned earlier.

Next, let’s execute some snippets.

How to execute statements

JShell has two input types:

  • Java code (which the JShell documentation refers to as snippets)
  • JShell commands

You can type any expression or statement at the jshell> prompt then press Enter to execute the code and see its results immediately.

Let me demonstrate how System.out.println works traditionally. The program requires a few lines of code and comments, which you have to write, compile, and execute. Even without the comments, five code lines were still required (lines 5 and 7–10).

 1  /*
 2   * Text-printing program.
 3   * author: Mohamed Taman.
 4   */
 5  public class Welcome {
 6     // main method begins execution of Java application”
 7   public static void main(String[] args) {
 8  System.out.println(("Welcome to Java Interactive Programming!");
 9     } // end method main
10   } // end class Welcome

In JShell, you can execute the statement in line 8 without creating all the infrastructure of class Welcome and its main method:

jshell>System.out.println("Welcome to Java Interactive Programming!")

In this case, JShell displays the snippet’s command-line output below the initial jshell> prompt and the statement you entered. Notice that I did not enter the preceding statement’s semicolon (;). JShell adds only terminating semicolons.

You need to add a semicolon if the end of the statement is not the end of the line — for example, if the statement is inside braces ({ }). Also, if there is more than one statement on a line then you need a semicolon between statements, but not after the last statement.

Not requiring semicolons is one example of how JShell re-interprets standard Java for convenient interactive use.

JShell keeps track of everything you type, which can be useful for re-executing prior statements and modifying statements to update the tasks they perform.

So now, let’s declare some variables explicitly.

Declaring variables explicitly

Almost anything you can declare in a typical Java source code file also can be declared in JShell.

For example, you can explicitly declare a variable and optionally assign a value and press Enter:

jshell>int number1 = 30
number1 ==> 30

When you enter a variable declaration, JShell displays the variable’s name (number1) followed by double equal and greater than signs (==> which means “has the value”) and the variable’s initial value (30).

In this section, you can explore the following variable techniques:

  • Avoid compilation errors in JShell
  • Fixing the error
  • Recalling and re-executing a previous snippet
  • Listing and executing prior snippets
  • Execute snippets by ID
  • Scratch variables
  • Using implicitly declared variables
  • Viewing a variable’s value
  • Resetting a JShell session
Avoid compilation errors in JShell

You must declare variables before using them in JShell. The following declaration of the int variable sum attempts to use a variable named number2 that has not been declared, so JShell reports a compilation error, indicating that the compiler was unable to find a variable named number2:

jshell>int sum = number1 + number2
|  Error:
|  cannot find symbol
|    symbol:   variable number2
|  int sum = number1 + number2;
|                      ^-----^

The error message uses the notation ^-----^ to highlight the error in the statement. No error is reported for the previously declared variable number1. Because this snippet has a compilation error, it’s invalid.

However, JShell still maintains the snippet as part of the JShell session’s history, which includes valid snippets, invalid snippets, and commands that you’ve typed.

You can recall this invalid snippet and execute it again later. JShell’s /history command displays the current session’s history; that is, everything you’ve typed:

jshell> /history
Fixing the error

Let’s fix the preceding error by first declaring number2 with the value 72:

jshell>var number2 = 72
number2 ==> 72
Recalling and re-executing a previous snippet

Now that both number1 and number2 are declared, we can declare the int variable sum. You can use the up and down arrow keys to navigate backward and forward through the snippets and JShell commands you’ve entered previously.

JShell recalls your prior inputs in reverse order; the last line of text you typed is recalled first. So, press the up-arrow key until sum’s prior declaration appears then you can press Enter to re-execute the snippet that declares and initializes sum:

jshell>int sum = number1 + number2
sum ==> 102
Listing and executing prior snippets

You can view a list of all previous valid Java code snippets with JShell’s /list command. JShell displays the snippets in the order you entered them:

jshell> /list

1 :System.out.println("Welcome to Java Interactive Programming!")
2 :int number1 = 30;
3 :var number2 = 72;
4 :int sum = number1 + number2;

Each valid snippet is identified by a sequential snippet ID. Note that /list may not display everything that /history does; /list is listing only valid statements.

Execute snippets by ID

You can execute any prior snippet by typing /id, which indicates the snippet’s ID. For example, when you enter:

jshell> /1
System.out.println("Welcome to Java Interactive Programming!")
Welcome to Java Interactive Programming!

JShell displays the first snippet you entered, executes it, and shows the result.

You can re-execute the last snippet you typed (whether it was valid or invalid) with:

jshell> /!

You can also refer to a range of IDs by separating the beginning ID and ending ID with a hyphen. For example, “1-4”would be the same as “1 2 3 4”. Look at this example:

jshell> /1-2 6

This executes the snippets from 1 to 2 and then snippet 6. Notice that the JShell command /reload can re-execute all existing snippets.

Scratch variables

When you enter an expression in JShell, it evaluates the expression, implicitly creates a variable, and assigns the expression’s value to the variable. Implicit variables are named $#,in which # is the new snippet’s ID. For example:

jshell> 11 + 5
$10 ==> 16

evaluates the expression 11 + 5 and assigns the resulting value (16) to the implicitly declared variable $10 because there were nine prior valid snippets. List them again:

jshell> /list

Notice that the implicitly declared variable $10 appears in the list simply as 10 without the $. This is another example of how JShell re-interprets standard Java language for interactive use. In regular Java programs, you must explicitly declare every variable.

Using implicitly declared variables

Like any other declared variable, you can use an implicitly declared variable in an expression. For example, the following code assigns the result of adding number1 (30) and $10 (16) to the existing variable sum:

jshell> sum = number1 + $10
sum ==> 46
Viewing a variable’s value

You can view a variable’s value at any time simply by typing its name and pressing Enter:

jshell> sum
sum ==> 46
Resetting a JShell session

You can remove all prior code from a JShell session by entering the /reset command:

jshell> /reset
|  Resetting state.

Then list again:

jshell> /list

The /list command shows that all prior snippets were removed.

At any time, you can clear a JShell session without exiting and running JShell again; just press control + command + L to clear a current JShell session.

Writing multi-line statements

Let’s write a switch expression statement that returns a string representation of the variable number:

jshell>var number = 2
number ==>2

Next, begin typing the switch expression statement:

jshell>switch(day){
   ...>

JShell knows that the switch expression statement is incomplete because we typed the opening left brace but did not provide a body or a closing right brace. JShell displays the continuation prompt ...> at which point you can enter more of the switch statement. The following completes and evaluates the if statement:

jshell> switch(day){
   ...> case 1 -> "One";
   ...> case 2 -> "Two";
   ...> default -> "Invalid Entry";
   ...> }
$3 ==> "Two"

In this case, a second continuation prompt appeared because the switch statement was still missing its terminating right brace (}).

Note: The statement-terminating semicolon (;) at the end of the case statement in the switch’s body is required.

There are two ways to edit code snippets:

  • To edit a single-line snippet, locate it with the up-arrow key, make your changes within the snippet, and press Enter to evaluate it
  • To edit a larger snippet that’s spread over several lines, such as an if statement that contains one or more statements, you can edit the entire snippet by using JShell’s /edit command to open the snippet in the JShell Edit Pad

Other kinds of multi-line statements include methods and classes.

Declaring a method

Pretend you’d like to write a method isPalindrome() that returns true if a given word is a palindrome or false if not:

jshell>booleanisPalindrome(String word) {
   ...>         return (new StringBuilder(word))
   ...>  .reverse()
   ...>  .toString()
   ...>  .equalsIgnoreCase(word);
   ...>     }
|  created method isPalindrome(String)
Invoking the declared method

Now declare a word variable as a string holding Madam as a value and invoke the isPalindrome() method:

jshell>var word = "Madam"
word ==> "Madam"

jshell>System.out.printf("The string {%s} is Palindrome!! %b %n", word, isPalindrome(word))
The string {Madam} is Palindrome!! True
Show all declared methods

You can use the /methods command to see the complete list of methods that are declared in the current JShell session.

Editing the current declared method

You can edit the isPalindrome snippet. And because isPalindrome is a multi-line snippet, the easiest way to edit it is using /edit ID command. You could use /list to determine the method snippet ID. You can also edit the method by specifying its name, which will open the JShell Edit Pad:

jshell> /edit isPalindrome
Creating a class

You create a class like you would any normal Java class. Though you can specify access modifiers like public on your classes (and other types), JShell ignores all access modifiers on the top-level types except for the abstract. A Person class could be created like the following:

jshell> class Person {
   ...>    private String name;
   ...>
   ...>    public void setName(String name) {
   ...>       this.name = name;
   ...>    }
   ...>
   ...>    public String getName() {
   ...>       return name;
   ...>    }
   ...> }
|  created class Person
Viewing declared classes

The /types command is used to view the classes you’ve declared so far; it displays all the types you declare, including classes, interfaces, and enums:

jshell> /types
|    class Person
Creating an instance of your class

There are two ways to create instances from the Person class. The following creates a person variable and initializes it:

jshell>var person = new Person()
person ==> Person@3d646c37

Or a scratch instance:

jshell> new Person()
$3 ==> Person@5a10411
Viewing declared variables

You can view all the variables you’ve declared so far with the following command:

jshell> /vars
|    Person person = Person@3d646c37
|    Person $3 = Person@5a10411
Using the instance

Once you have an object, you can call its methods by typing pe then press the Tab key to auto-complete the variable name for you, then type .se and the Tab key again and it will complete the method for you:

jshell>person.setName("Mohamed Taman")

Now to get the value, again type pe then Tab then .ge followed by Tab; this time it will show that there are more methods matched, so complete with letter s and Tab and Enter:

jshell> person.ge
getClass()   getName()
jshell>person.getName()
$5 ==> "Mohamed Taman"

The Tab key auto-complete feature works with almost everything including commands like /list.

Automatic creation of a meaningful variable name

While recalling the last snippet:

jshell>person.getName()

press both the Shift and Tab keys together, then release those keys and press v:

jshell>String  _= person.getName()

JShell positions the cursor (indicated by the underscore) immediately before the = sign so you can simply type the variable name.

Saving and opening code-snippet files

Now it is time to save all your experimental snippets into a file. You can save all of a session’s valid code snippets to a file, which you can then load into a JShell session as needed.

Saving snippets to a file

To save just the valid snippets, use the /save command:

jshell>/save filename

By default, the file is created in the folder from which you launched JShell. To store it in a different location, just specify the complete path of the file.

Loading snippets from a file

Once you save your snippets, they can be loaded again with the /open command, which will execute each snippet in the file.

jshell>/open filename

Also, you can load Java source code files:

jshell>/open filename.java

Exceptions handling

Type the following code snippet and press Enter:

jshell>int[] values = {10, 20, 30}
values ==>int[3] { 10, 20, 30 }

jshell>values[4]
|  Exceptionjava.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 3
|        at (#5:1)

jshell>

In JShell, manually catching exceptions is not required. JShell automatically catches each exception and displays information about it then displays the next JShell prompt so you can continue your session.

By automatically catching all exceptions, JShell makes it easier for you to experiment with methods that throw checked exceptions.

Importing packages

To list all the imported packages in a current JShell session, you use the following command:

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.*

You can also import the package yourself. In the following example, I will import a data and time API:

jshell> import java.time.*

Type Loca then press the Tab key for auto-complete:

jshell>Loca
LocalDateLocalDateTimeLocalTime       Locale

jshell>LocalTime.now(
Signatures:
LocalTimeLocalTime.now()
LocalTimeLocalTime.now(ZoneId zone)
LocalTimeLocalTime.now(Clock clock)

<press tab again to see documentation>

jshell>LocalTime.now()
$8 ==> 18:55:13.740201

Note: If you know the class but don’t know which package it is, you can type the full class name and press both the Shift and Tab keys together then release those keys and press “i” and this will give you suggested packages to import from:

jshell>LocalTime
0: Do nothing
1: import: java.time.LocalTime
Choice: 1
Imported: java.time.LocalTime
jshell>LocalTime

Using the /help function

To learn about all JShell commands, type the /help command:

jshell> /help
|  Type a Java language expression, statement, or declaration.
|  Or type one of the following commands:
|  /list [<name or id>|-all|-start]
|      list the source you have typed
|  /edit <name or id>
|      edit a source entry
|  /drop <name or id>
|      delete a source entry
|  /save [-all|-history|-start] <file>
|      Save snippet source to a file
|  /open <file>
|      open a file as source input
|  /vars [<name or id>|-all|-start]
|      list the declared variables and their values
|  /methods [<name or id>|-all|-start]
|      list the declared methods and their signatures
|  /types [<name or id>|-all|-start]
|      list the type declarations
|  /imports
|      list the imported items
|  /exit [<integer-expression-snippet>]
|      exit the jshell tool
|  /env [-class-path <path>] [-module-path <path>] [-add-modules <modules>] ...
|      view or change the evaluation context
|  /reset [-class-path <path>] [-module-path <path>] [-add-modules <modules>]...
|      reset the jshell tool
|  /reload [-restore] [-quiet] [-class-path <path>] [-module-path <path>]...
|      reset and replay relevant history -- current or previous (-restore)
|  /history [-all]
|      history of what you have typed
|  /help [<command>|<subject>]
|      get information about using the jshell tool
|  /set editor|start|feedback|mode|prompt|truncation|format ...
|      set configuration information
|  /? [<command>|<subject>]
|      get information about using the jshell tool
|  /!
|      rerun last snippet -- see /help rerun
|  /<id>
|      rerun snippets by ID or ID range -- see /help rerun
|  /-<n>
|      rerun n-th previous snippet -- see /help rerun
|
|  For more information type '/help' followed by the name of a
|  command or a subject.
|  For example '/help /list' or '/help intro'.
|
|  Subjects:
|
|  intro
|      an introduction to the jshell tool
|  keys
|      a description of readline-like input editing
|  id
|      a description of snippet IDs and how use them
|  shortcuts
|      a description of keystrokes for snippet and command completion,
|      information access, and automatic code generation
|  context
|      a description of the evaluation context options for /env /reload and /reset
|  rerun
|      a description of ways to re-evaluate previously entered snippets

Exiting JShell

To terminate the current JShell session, use the /exit command or press the keyboard shortcut Ctrl + d (control + d on MacOS).

jshell> /exit
|  Goodbye
mohamed_taman:~$

Summary

You have learned how to use JShell to effectively explore new Java features, APIs, prototype methods and classes, and most of the commands.

This tutorial has a second part, where I will demonstrate how to use the auto-complete feature effectively so that you can speed up your exploring and learning processes. Also, I’ll show you how to add external JAR files to a current JShell session CLASS-PATH and how to add modules to the current MODULE-PATH.