Java 8 through 11 – part 1 (JShell)

data: 26 października, 2018
czas czytania: 6 min
autor: Szymon Sobocik

A lot of changes were introduced in the Java development ecosystem last year, starting from Oracle announcing a new approach to releasing Java versions, through releasing three new versions (9, 10 and 11) and ending with the controversial news on introducing fees for using certain releases. This caused some developers to stay in their comfort zone with “safe and tested” Java 8.  In this and the following articles, I would like to present some of the new features introduced in Java since version 8 and analyse what they offer and how they affect us, developers.

JShell

JShell is a new feature of JDK that allows developers to quickly evaluate and validate some ideas by writing a few lines of code ad hoc, directly into the console and to get the results immediately. Before Java 9, if someone wanted just to try something, they were forced to write quite a lot of boilerplate, then compile it and run. For example, to write a simple HelloWorld, one had to create a new file, define a class, and write the main method with the body:

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

Once done, a manual compilation and execution were required:

C:\work\my\public\my-examples\src>javac HelloWorld.java
C:\work\my\public\my-examples\src>java HelloWorld
Hello World

Achieving the same result with JShell is much faster. You simply write:

C:\work\my\public\my-examples\src>jshell
|  Welcome to JShell -- Version 9.0.4
|  For an introduction type: /help intro

jshell> System.out.println("Hello World")
Hello World

When you enter jshell into your command prompt, JShell creates a new context for you in which you can experiment with the Java code as it was defined in a code block.

I recommend starting with the command:

/help

which will present a list of commands accepted by JShell.

Now, let’s write something more advanced like a simulator of a dice. A traditional dice, a cube with 6 different values on its sides.

First, we need to define a list of possible outcomes from the throw:

jshell> String[] outcomes = new String[]{"One", "Two", "Three", "Four", "Five", "Six"}
outcomes ==> String[6] { "One", "Two", "Three", "Four", "Five", "Six" }

jshell> Random r = new Random()
r ==> java.util.Random@3c0f93f1

As you can see, we’ve defined an array of Strings, as we would’ve done in a method. We’ve also defined an `r` variable of a Random type.

To display the list of all defined variables you can type:

jshell> /vars
|    String[] outcomes = new String[]{"One", "Two", "Three", "Four", "Five", "Six"}
|    Random r = java.util.Random@3c0f93f1

Let’s cast some throws:

jshell> outcomes[r.nextInt(outcomes.length)]
$3 ==> "One";
 
jshell> outcomes[r.nextInt(outcomes.length)]
$4 ==> "Five";
 
jshell> outcomes[r.nextInt(outcomes.length)]
$5 ==> "Six";
 
jshell> outcomes[r.nextInt(outcomes.length)]
$6 ==> "Six";

As you can see, each of the results was assigned to a newly created variable the name of which starts with a dollar sign ($).
If we display all variables, we can see that they are normal ones and we can use them as any other variable in the following statements.

jshell> /vars
|    String[] outcomes = String[6] { "One", "Two", "Three", "Four", "Five", "Six" }
|    Random r = java.util.Random@3c0f93f1
|    String $3 = "One";
|    String $4 = "Five";
|    String $5 = "Six";
|    String $6 = "Six";

jshell> System.out.println(String.format("The results are: %s, %s, %s and %s", $3, $4, $5, $5))
The results are: One, Five, Six and Six

Let’s extract the line that holds the logic of calculating next outcome into a method.

If we want to define a method which normally would be written in multiple lines of code, we can use one of the JShell features for it. When you press enter after opening a code block (starting with `{` character), it won’t evaluate immediately but it will allow entering more entries until you finish the block with a closing bracket ( `}` ).

jshell> String throwDice() {
   ...> return outcomes[r.nextInt(outcomes.length)];
   ...> }
|  created method throwDice()

From now on, we can just call it a `throwDice` method.

jshell> throwDice()
$12 ==> "Four";

jshell> throwDice()
$13 ==> "One";
 
jshell> throwDice()
$14 ==> "Four";

Saving your work

At any point in time you can look at all lines (snippets) that have been entered so far with the /list command:

jshell> /list
   1 : String[] outcomes = new String[]{"One", "Two", "Three", "Four", "Five", "Six"};
   2 : Random r = new Random();
   3 : outcomes[r.nextInt(outcomes.length)]
   4 : outcomes[r.nextInt(outcomes.length)]
   5 : String throwDice() {
       return outcomes[r.nextInt(outcomes.length)];
       }
   6 : throwDice()
   7 : throwDice()

What if we want to save pieces of code developed with JShell? We can simply run the `/save` command to save snippets to a file for later use.

jshell> /save my-snippets

jshell> /save my-snippets

This way we will create a new file called my-snippets containing all lines and pieces of the code entered so far.
To open a saved snippet just run:

jshell> /open my-snippets

After calling this line you can continue working as all entries from a snippet file have been invoked.

Forward reference

As JShell is meant mainly for experimenting and prototyping, it would be difficult to predict all methods and variables in advance and before you evaluate the actual statement.

JShell will allow you to define a method using variables which haven’t been defined yet in the scope. If you define them later, the method will start working. For example:

jshell> double add() {
   ...> return a + b;
   ...> }
|  created method add(), however, it cannot be invoked until variable a, and variable b are declared
 
jshell> add()
|  attempted to call method add() which cannot be invoked until variable a, and variable b are declared
 
jshell> double a = 10
a ==> 10.0
 
jshell> double b = 20
b ==> 20.0
 
jshell> add()
$5 ==> 30.0

Exceptions

There is also a built-in support for handling exceptions.

jshell> String toWord(Integer number){
   ...>    return numbers[number-1];
   ...> }
|  created method toWord(Integer), however, it cannot be invoked until variable numbers is declared
 
jshell> String[] numbers = new String[]{"One", "Two", "Three", "Four", "Five", "Six"}
numbers ==> String[6] { "One", "Two", "Three", "Four", "Five", "Six" }
 
jshell> System.out.println(toWord(1))
One
 
jshell> System.out.println(toWord(4))
Four
 
jshell> System.out.println(toWord(9))
|  java.lang.ArrayIndexOutOfBoundsException thrown: 8
|        at toWord (#1:2)
|        at (#5:1)

We’ve defined a method that translates Integer into a word. But it does not check for a valid argument. When passing a not supported argument, we get an exception with a stacktrace informing in which method and line it has occurred.

Summary

JShell is a very useful tool for a quick validation of ideas and prototyping, with much more features than described in this post. You can find out more in the Oracle’s introduction to JShell here. In the following posts, we’ll discuss other interesting tools and features that have come to the Java world since the release of Java 9. Stay tuned!

Newsletter IT leaks

Dzielimy się inspiracjami i nowinkami z branży IT. Szanujemy Twój czas - obiecujemy nie spamować i wysyłać wiadomości raz na dwa miesiące.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.

Subscribe to our newsletter

Administratorem Twoich danych osobowych jest Future Processing S.A. z siedzibą w Gliwicach. Twoje dane będziemy przetwarzać w celu przesyłania cyklicznego newslettera dot. branży IT. W każdej chwili możesz się wypisać lub edytować swoje dane. Więcej informacji znajdziesz w naszej polityce prywatności.