Skip to content

Latest commit

 

History

History

session-3-jshell

JShell

Java REPL (Read-Eval-Print-Loop): previously called Kulla.

The JShell API can be used via applications as well.

Windows Users

Check if JAVA_HOME is set on your machine

c:\... echo %JAVA_HOME%
"c:\Program Files\Java\jdk-9\"

Look for the JShell REPL in the JAVA_HOME\bin folder:

c:\... dir /W %JAVA_HOME%\bin\js*

Directory of C:\Program Files\Java\jdk-9\bin

jshell.exe     jsound.dll     jsoundds.dll   jstack.exe     jstat.exe
jstatd.exe

And locate the command from the above list. Ensure JAVA_HOME\bin is in your PATH.

c:\... echo %PATH%
c:\Program Files\Java\jdk-9\bin\;....

If JAVA_HOME is not set on your Windows machine (or it's not set to Java 9) or PATH doesn't show the bin folder for JDK-9, You should set the correct value by following these steps:

  • Invoke the Control Panel, and then System.
  • Click Advanced and then Environment Variables.
  • Add the location of the JDK-9 folder to the JAVA_HOME,
  • and add or edit the location of the bin folder of the JDK installation to PATH variable in System Variables.

You should set the JAVA_HOME value between quotation marks.

Once JAVA_HOME and PATH are set, you must close the current console and then open a new Windows Console. Now you are ready to start JShell with the following command:

c:\... jshell

MacOs / Linux Users

Look for the JShell REPL in the JAVA_HOME/bin folder:

$ ls $JAVA_HOME/bin/

appletviewer  java     javapackager  jdb        jinfo  jps         jstatd        policytool   serialver   wsimport
idlj          javac    javaws        jdeprscan  jjs    jrunscript  jweblauncher  rmic         servertool  xjc
jaotc         javadoc  jcmd          jdeps      jlink  [jshell]    keytool       rmid         tnameserv
jar           javah    jconsole      jhsdb      jmap   jstack      orbd          rmiregistry  unpack200
jarsigner     javap    jcontrol      jimage     jmod   jstat       pack200       schemagen    wsgen

And locate the command from the above list. Ensure JAVA_HOME/bin is in your PATH.

$ echo $PATH
/usr/lib/jvm/jdk-9/bin/:....

If JAVA_HOME is not set on your Mac or Linux machine (or it's not set to Java 9) or PATH doesn't show the bin folder for JDK-9, You should set the correct values by following the next steps: (For Mac Users the JDK 9 path could be /Library/Java/JavaVirtualMachines/jdk9/Contents/Home)

$ export JAVA_HOME=/usr/lib/jvm/jdk-9/
$ export PATH=/usr/lib/jvm/jdk-9/bin:$PATH

Once JAVA_HOME and PATH are set you can start JShell with the next command

$ jshell

After you start JShell (Windows, MacOs and Linux Users) you get taken to the JShell prompt:

|  Welcome to JShell -- Version 9-ea
|  For an introduction type: /help intro

jshell> 

The first command to try in the REPL:

jshell> /?

Let's assign a string to a variable and see its effect:

jshell> String name = "ICELAND-JUG"
name ==> "ICELAND-JUG"

Define a void method and execute it:

jshell> void hello() { System.out.println("Hi " + name); }
|  modified method hello()

jshell> hello()
Hi ICELAND-JUG

jshell> hello();
Hi ICELAND-JUG

As you can see there is no need to use a semi-colon at the end of single line commands in the REPL.

By default, the REPL loads with pre-defined imports:

jshell> /i
|    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.*

Now create your own variables, method, etc.. and play with them in the REPL.

Try using the /v or the /m REPL commands, here's a sample output:

jshell> /v
|    String name = "ICELAND-JUG"
|    int i = 10
|    int j = 15
|    int k = 25

jshell> /m
|    void hello()

Now try some more REPL commands, at the jshell prompt:

/vars 
/list
/methods 
/edit hello
/imports

As mentioned earlier, Java 9 provides a JShell API that we can access from within our application:

   $ jshell --add-modules jdk.jshell
    jshell> import jdk.jshell.*;
    
    jshell> JShell shell = JShell.create();
    shell ==> jdk.jshell.JShell@3b2c72c2
    
    jshell> shell.eval("int x = 5;");
    $3 ==> [SnippetEvent(snippet=Snippet:VariableKey(x)#1-int x = 5;,previousStatus=NONEXISTENT,status=VALID,isSignatureChange=true,causeSnippetnullvalue=5)]
    
    jshell> Stream<VarSnippet> vars = shell.variables();
    vars ==> java.util.stream.ReferencePipeline$3@7674b62c

The above block is not for the REPL.

Some more Java 9 improvements to try in JShell (thanks to @sjmaple for the contribution)

jshell> import java.time.*;
jshell> void printf(Object a) { 
    System.out.println(a); 
}

|  created method printf(Object)

Collections of factory methods:

1-10 arguments + varargs:

jshell> List.of(1, 2, 3, 4, 5)
$3 ==> [1, 2, 3, 4, 5]

jshell> Map.of("hello", "world", "status", "mad")
$4 ==> {hello=world, status=mad}

jshell> Set.of(1,2,4)
$5 ==> [2, 4, 1]

jshell> Set.of(1,2,4)      <=== no change in order of the elements
$6 ==> [2, 4, 1]

Implementation classes 2 elements vs N elements:

jshell> List.of(1).getClass()
$9 ==> class java.util.ImmutableCollections$List1

jshell> List.of(1, 2).getClass()
$10 ==> class java.util.ImmutableCollections$List2

Streams:

  • dropwhile/takewhile:
jshell> IntStream.range(1, 10).dropWhile(x -> x < 5).forEach(System.out::println)

5
6
7
8
9
  • iterate
jshell> IntStream.iterate(0, x -> x + 1).filter(x -> x < 10).forEach(System.out::println)

0
1
2
3
4
5
6
7
8
9

Press Ctrl-C to get back to the jshell prompt

  • Stream from Optional:
jshell> Optional.of(1).stream().map(x -> x*3);
$16 ==> java.util.stream.ReferencePipeline$3@4e7dc304
 
jshell> Optional.empty().or(() -> Optional.of("Hello"))
$17 ==> Optional[Hello]

jshell> Optional.empty().ifPresent(null) 

jshell> Optional.empty().ifPresentOrElse(null, () -> printf("hello"));
hello
  • Date and time improvements:
jshell> import java.time.*;

jshell> LocalDate.now()
$21 ==> 2017-08-15

jshell> $21.datesUntil(LocalDate.of(2017, 8, 20)).forEach(System.out::println)
2017-08-15
2017-08-16
2017-08-17
2017-08-18
2017-08-19

Concurrency

  • Completable futures:
jshell> CompletableFuture<String> future = new CompletableFuture<>()
future ==> java.util.concurrent.CompletableFuture@77846d2c[Not completed]

jshell> future.copy()
$28 ==> java.util.concurrent.CompletableFuture@16f7c8c1[Not completed]

jshell> $28.complete("Hello world")
$29 ==> true

jshell> $29.get()
|  Error:
|  boolean cannot be dereferenced
|  $29.get()
|  ^-----^

jshell> future.isDone()
$30 ==> false
  • delays / timeouts:
* completeOnTimeout(T value, long timeout, TimeUnit unit)

    jshell> future.completeOnTimeout("Isn't this amazing", 1, TimeUnit.SECONDS)
    $31 ==> java.util.concurrent.CompletableFuture@281e3708[Not completed, 2 dependents]
 
* orTimeout(1, TimeUnit.SECONDS)
 
    jshell> future.orTimeout(1, TimeUnit.SECONDS)
    $32 ==> java.util.concurrent.CompletableFuture@281e3708[Completed normally]

HttpClient

Run jshell with a CLI argument to load a JDK module (jdk.incubator.httpclient)

$ jshell --add-modules jdk.incubator.httpclient

jshell> import jdk.incubator.http.*;
jshell> String body = HttpClient.newHttpClient().send(HttpRequest.newBuilder(URI.create("https://www.google.com")).GET().build(), HttpResponse.BodyHandler.asString()).body();

Try using http in the URL address? What is the expected result?

jshell> String body = HttpClient.newHttpClient().send(HttpRequest.newBuilder(URI.create("http://www.google.com")).GET().build(), HttpResponse.BodyHandler.asString()).body();
body ==> "<HTML><HEAD><meta http-equiv=\"content-type\" co ... A>.\r\n</BODY></HTML>\r\n"

Try without https in the URL address? What is the expected result?

java.lang.NullPointerException

Now that we can make http calls via the REPL, lets have a look at how that works. We can use Java 9's new API to create Http calls. Check the JavaDoc for HttpRequest or use the example above. Make 50 http GET calls to http://www.google.com via the REPL.

Process management API:

Reload jshell using the usual CLI invocation:

$ jshell

jshell> ProcessHandle currentProcess = ProcessHandle.current();
currentProcess ==> 10420

jshell> System.out.println("Current Process Id: = " + currentProcess.pid());
Current Process Id: = 10420
  • ProcessHandle.Info info()
    • Optional<String[]> arguments()
    • Optional command()
    • Optional commandLine()
    • Optional startInstant()
    • Optional totalCpuDuration()
    • Optional user()
jshell> currentProcess.info()
$5 ==> [user: Optional[username], cmd: /usr/lib/jvm/jdk-9/bin/java, args: [-agentlib:jdwp=transport=dt_socket,address=localhost:40499, jdk.jshell.execution.RemoteExecutionControl, 41667], startTime: Optional[2017-08-15T17:36:30.240Z], totalTime: Optional[PT0.88S]]
  • pid, info, children processes, destroy processes:
jshell> ProcessHandle currentProcess = ProcessHandle.current();
currentProcess ==> 10420

jshell> currentProcess.pid()
$7 ==> 10420

jshell> ProcessHandle.of(10420)
$9 ==> Optional[10420]

jshell> $9.get()
$10 ==> 10420

jshell> $10.info()
$11 ==> [user: Optional[username], cmd: /usr/lib/jvm/jdk-9/bin/java, args: [-agentlib:jdwp=transport=dt_socket,address=localhost:40499, jdk.jshell.execution.RemoteExecutionControl, 41667], startTime: Optional[2017-08-15T17:36:30.240Z], totalTime: Optional[PT1.09S]]

jshell> Stream<ProcessHandle> childProc = ProcessHandle.current().children();
childProc ==> java.util.stream.ReferencePipeline$2@21213b92

jshell> Process process = Runtime.getRuntime().exec ("/bin/bash");
process ==> Process[pid=709, exitValue="not exited"]

jshell> childProc.count()
$47 ==> 1

jshell> Stream<ProcessHandle>  childProc = ProcessHandle.current().children();
childProc ==> java.util.stream.ReferencePipeline$2@10d59286

jshell> childProc.forEach(procHandle -> { System.out.println(procHandle.destroy() ? "Could not kill process " + procHandle.pid() : "Terminated " + procHandle.pid()); });
Could not kill process 709

or

Terminated 709
    
or

|  java.lang.IllegalStateException thrown: stream has already been operated upon or closed
|        at AbstractPipeline.evaluate (AbstractPipeline.java:229)
|        at ReferencePipeline.forEach (ReferencePipeline.java:430)
|        at (#62:1)

StackWalker:

jshell> StackWalker.getInstance().walk(s -> s.limit(5).collect(Collectors.toList()));
[do_it$(java:15), jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:209), jdk.jshell/jdk.jshell.execution.RemoteExecutionControl.invoke(RemoteExecutionControl.java:116), jdk.jshell/jdk.jshell.execution.DirectExecutionControl.invoke(DirectExecutionControl.java:119), jdk.jshell/jdk.jshell.execution.ExecutionControlForwarder.processCommand(ExecutionControlForwarder.java:134)]

Stop doing new Exception when you need to inspect the stack while in the REPL.

JDK structure changes

  • Semantic versioning:
jshell> Runtime.version()
$13 ==> 9-ea+170-jigsaw-nightly-h6410-20170518

jshell> $13.major()
$14 ==> 9

jshell> $13.minor()
$15 ==> 0

Language improvements continue (Project Coin):

_ is an invalid identifier (for lambdas)

 jshell> int _ = 1
 |  Error:
 |  as of release 9, '_' is a keyword, and may not be used as an identifier
 |  int _ = 1
 |      ^
 |  Error:
 |  reached end of file while parsing
 |  int _ = 1
 |           ^

 jshell> int __ = 1
 __ ==> 1

private methods in interfaces:

 jshell> interface A { private int zero() { return 0;} default int one() { return zero() + 1;}}
 |  created interface A

effectively final in try with resources:

 jshell> boolean a() throws Exception { Socket s = new Socket(); return s.isClosed(); }
 |  created method a()

 jshell> boolean a() throws Exception { Socket s = new Socket(); try (s) { } return s.isClosed(); }
 |  modified method a()

UTF-8 property files: Create a text file call properties.properties containing, save the file and load jshell:

    # This is the default properties.properties file
    s1 = computer
    s2 = disk
    s3 = monitor
    s4 = keyboard

 jshell> new PropertyResourceBundle(new FileInputStream("properties.properties")).handleGetObject("s2")
 $1 ==> "computer"    

Finally, exit the REPL via one of the two ways:

jshell> /exit
|  Goodbye

Or

jshell> [Ctrl][D]     
(quietly exits)

Read all about the Java REPL at

Follow Hendrik's hands-on presentation on JShell to learn about intermediate and advanced features: https://youtu.be/y868lMk6NtY?t=13156

Watch this interesting tutorial from Heinz Kabutz.

Try some more JShell examples:

Some online Java REPLs to play with:

Credits