One of the tools that Java developers wanted to see in the development kit was a tool that had been present in other programming languages (ipython, erb) for a long time - an interactive shell. Thankfully, this interactive Java Shell tool, known as JShell, was added in the JDK 9 release (September 2017) and has since become one of its most acclaimed features.

Introduction

JShell is also known as Read-Evaluate-Print Loop (REPL). The name is self-explanatory: the tool reads user input, evaluates the expressions, and prints the outputs in a continuous loop, usually from the command line (terminal).

One of the big advantages of JShell is that it gives you the ability to quickly evaluate expressions, testing pieces of code one at a time interactively without having to use Java's regular compile flow. The regular process would involve: writing a class with a main method, compiling it, checking whether there are any compile errors, and then running the compiled class to get the output. 

On the other hand, JShell allows developers to test individual statements, functions, method variations, and unfamiliar libraries almost instantaneously. Therefore, building software with known behavior becomes much faster.

Installing JShell

In order to install JShell on your local machine, you will need to install JDK 9 or any subsequently released version. Note that Oracle currently does not support Java 9 or 10 anymore, because they were not meant to be LTS (long-term supported) versions. So if you visit the official download page, these versions are not available for download.

Let's proceed by installing either Java 11 or 14.  This can be done manually by downloading from their website or by using your OS package manager (brew, apt-get). As a side note, if you need to have another version of Java installed on your machine, you can switch the versions using JEnv, SDKMAN, or Jabba, or by manually setting the JAVA_HOME environment variable.

Basic Usage

After the installation is complete, we are ready to use JShell. Starting and stopping it is pretty straightforward:

➜  ~ jshell

|  Welcome to JShell -- Version 13.0.2

|  For an introduction type: /help intro

 

jshell> /exit

|  Goodbye

Note: If you want more details on each operation, start JShell in verbose mode (-v)

VARIABLES, METHODS, AND CLASSES:

 

jshell> int x = 10;

x ==> 10

|  created variable x : int

 

jshell> double y = 20;

y ==> 20.0

|  replaced variable y : double

 

jshell> x + y

$7 ==> 30.0

|  created scratch variable $7 : double

You may have observed that when evaluating expressions, JShell creates a scratch variable and assigns the value to it ($7); that variable can be used later by simply typing its name. Another difference that you may have observed is that it's not necessary to add a semicolon at the end of statements - JShell will automatically add it for you.

If you're using Java 10+ (which you probably are), you can also take advantage of the new `var` resource. This will allow you to create variables that will have their types inferred according to the value passed.

 

jshell> var anotherString = "Daniel"

anotherString ==> "Daniel"

|  created variable anotherString : String

 

jshell> var myNumber = 10

myNumber ==> 10

|  created variable myNumber : int

 

jshell> var anotherNumber = 10d

anotherNumber ==> 10.0

|  created variable anotherNumber : double

To declare functions, follow the same structure as above:

 

jshell> String reverse(String myString){

   ...> return new StringBuilder(myString).reverse().toString();

   ...> }

|  created method reverse(String)

 

jshell> reverse("MONDAY")

$23 ==> "YADNOM"

|  created scratch variable $23 : String

 

You can replace variables and methods by simply declaring them again. Also, take advantage of JShell's auto-complete by typing <tab> as many times you want. This works for Java native packages, display signatures, documentation, and even data inserted by the user in the current session.

Another very useful resource is the ability to declare methods and classes with contained values that were not declared before. This resource is also called Forward Reference.

 

jshell> BigDecimal taxOverSalary(BigDecimal salary){

   ...> return salary.multiply(COUNTRY_MONTHLY_TAX_RATE);}

|  created method taxOverSalary(BigDecimal), however, it cannot be invoked until variable COUNTRY_MONTHLY_TAX_RATE is declared

|    update overwrote method taxOverSalary(BigDecimal)

 

jshell> taxOverSalary(new BigDecimal("1000"));

|  attempted to call method taxOverSalary(BigDecimal) which cannot be invoked until variable COUNTRY_MONTHLY_TAX_RATE is declared

 

jshell> BigDecimal COUNTRY_MONTHLY_TAX_RATE = new BigDecimal("0.001")

COUNTRY_MONTHLY_TAX_RATE ==> 0.001

|  created variable COUNTRY_MONTHLY_TAX_RATE : BigDecimal

|    update modified method taxOverSalary(BigDecimal)

 

jshell> taxOverSalary(new BigDecimal("1000"));

$37 ==> 1.000

|  created scratch variable $37 : BigDecimal

 

One important point here is that if you update the type of a variable later on, that will break the method at runtime. You can only view whether a method may cause future errors in verbose mode.

To create classes, follow the same examples from above:

 

jshell> class Pet {

   ...>

   ...> public String name;

   ...> private int age;

   ...>

   ...> public void setAge(int age){

   ...>     this.age = age;

   ...> }

   ...> public int getAge(){

   ...> return this.age;

   ...>

   ...> }}

 

<press tab again to see documentation>

jshell> p.

equals(       getAge() getClass()    hashCode() name notify()      notifyAll()

setAge(       toString() wait(

jshell> p.setAge(10)

 

jshell> p.name = "Toddy"

$63 ==> "Toddy"

|  created scratch variable $63 : String

 

Commands

In JShell, there are some native commands that will help you a lot during development:

/vars : displays all variables in the session
  • /methods : displays all methods in the session
  • /types : displays all classes and interfaces declared
  • /list : displays all snippets previously run (any valid piece of code run is a snippet)
  • /save : saves your snippets (/save ~/mySession.jsh)
  • /open : opens an external snippet (/open ~/test.java or /open ~/mySession.jsh)
  • /reset : clears current session (all snippets) 
  • /edit : opens current snippets on your preferred editor (/set editor kwrite - to update the default external editor or /edit 1 - to edit last snippet or /edit MyClass - to edit specific class)
  • /imports : shows the current list of imports (JShell will automatically import java.io, math, and util on startup)
  • /set : allows you to configure many things (/set feedback (verbose|concise|normal|silent) - change feedback mode) : More details can be found here
    • /help : can be used before any other command to go through documentation
      Tab can also be used here for auto-completion (just type / + Tab to the see the full list)
  • You can use abbreviations for commands. Eg: If there is only one command that starts with L, you can type /l instead of /list. For the /save command, for instance, since there is also a /set command, you will need to type /sa or /se.
  •  
Useful Shortcuts

Most JShell shortcuts are the same resources available in several other interactive shells.

  • To navigate through history: use the up and down arrows
  • To extract an expression into a variable: (Shift + Tab) v
  • To help on import statements: (Shift + Tab) i -> This needs the class name to be written on the console
  • To reverse search on previous snippets : (Ctrl + R) + 'search term'
  • To forward search on snippets : (Ctrl + S) + 'search term'
  •  
Downloading External Libraries

To take advantage of using external libraries, download the jar files and place them in /opt/libs, then add the folder to your classpath in one of the following ways:

1. Add the libraries to the classpath:

jshell --class-path /opt/libs/guava-19.0.jar:/opt/libs/commons-lang3-3.4.jar

2. Change evaluation context within the current session:

jshell> /env -class-path /opt/libs/commons-lang3-3.4.jar:/opt/libs/guava-19.0.jar

After following one of the steps above, you will be able to import third-party libraries as is:

jshell> import org.apache.commons.lang3.StringUtils

 

Conclusion

As you may have observed, Oracle has put a lot of effort into building a very complete interactive shell that provides a lot of resources. It should be used mostly for fast prototyping and experimentation, since it provides instantaneous feedback without the need for compilation (which may take some time depending on the project size). It does not replace IDEs - these should actually be used together. 

I also think JShell is a great tool for educational purposes, as it is easy to use and helps people who are starting to learn programming or Java.

In my personal opinion, JShell has two main target audiences:

1) People who are learning programming logic or Java - it is a fantastic tool due to its simplicity and how easy it is to use.

2) Most experienced developers - might need some time to get the hang of it, but after gaining proficiency with combining commands and using auto-complete with the preferred editor side by side, it will boost productivity. 

I also think that it could be easier to import external libraries (you can try this alternative) and that editing big portions of code within the shell might not be helpful since it works very much like emacs, which is not unanimous among developers.

 

References

For further reading and resources, check out these articles:

Oracle documentation

JDK downloads

Jenv trouble shooting

Jenv configuration


Author

Daniel Vilas-Boas

Daniel is a Software Engineer and Scrum Master at Avenue Code. Currently based in São Paulo, he is also involved in AI researching, especially machine learning and natural language processing. During his free time, he supports his soccer team – Náutico.


Introducing JetBrains Academy

READ MORE

Vert.x is a Great Framework, and I Can Prove It

READ MORE

Event-Driven Architecture and Its Application in Java - Part 2

READ MORE