{"id":3800,"date":"2017-03-24T19:46:23","date_gmt":"2017-03-24T17:46:23","guid":{"rendered":"http:\/\/www.tutego.de\/blog\/javainsel\/?p=3800"},"modified":"2017-03-24T19:46:23","modified_gmt":"2017-03-24T17:46:23","slug":"jshell-die-interaktive-repl-shell-in-java-9","status":"publish","type":"post","link":"https:\/\/www.tutego.de\/blog\/javainsel\/2017\/03\/jshell-die-interaktive-repl-shell-in-java-9\/","title":{"rendered":"JShell, die interaktive REPL-Shell in Java 9"},"content":{"rendered":"<p>Im JDK 9 ist ein neues Programm eingezogen: die JShell. Mit ihr lassen sich auf einfache Weise kleine Java-Programme und einzelne Anweisungen testen, sogenannte Snippets, ohne eine gro\u00dfe IDE starten zu m\u00fcssen. Die JShell ist eine Befehlszeile (Shell), die nach dem Read-Evaluate-Print-Loop-Prinzip arbeitet:<\/p>\n<ul>\n<li>Read (Lesen): Eingabe des Programms von der Kommandozeile. Eine gute Shell bietet eine Historie der letzten Kommandos und Tastaturvervollst\u00e4ndigung.<\/li>\n<li>Eval (Ausf\u00fchren): Compiliert das Snippet und f\u00fchrt es aus.<\/li>\n<li>Print (Ausgaben): Die der Anweisungen und Programme werden in der Umgebung ausgegeben.<\/li>\n<li>Loop (wiederholen): Es folgt ein R\u00fccksprung auf den Zustand Lesen.<\/li>\n<\/ul>\n<p>Das bekannteste Beispiel f\u00fcr eine REPL-Umgebung ist die Unix-Shell. Doch viele Skriptsprachen wie Lisp, Python, Ruby, Groovy und Clojure bieten solche REPL-Shells. Nun auch Java seit Version 9. Die R\u00fcckmeldung ist schnell, und gut zum Lernen und Ausprobieren von APIs.<\/p>\n<p>Im <em>bin<\/em>-Verzeichnis vom JDK finden wir das Programm jshell. Rufen wir sie auf:<\/p>\n<pre>|\u00a0 Welcome to JShell -- Version 9-ea\n\n|\u00a0 For an introduction type: \/help intro\n\n\n\n\njshell&gt;<\/pre>\n<p>Die JShell besitzt eingebaute Kommandos, die mit \/ beginnen, um zum Beispiel alle deklarierten Variablen ausgeben oder das Skript speichern. \/help gibt eine Hilfe \u00fcber alle Kommandos, \/exit beendet JShell.<\/p>\n<p>Nach dem Start wartet JShell auf die Snippets. G\u00fcltig sind:<\/p>\n<ul>\n<li>Import-Deklarationen<\/li>\n<li>Typ-Deklarationen<\/li>\n<li>Methoden-Deklarationen<\/li>\n<li>Anweisungen<\/li>\n<li>Ausdr\u00fccke<\/li>\n<\/ul>\n<p>Es sind also Teilmengen der Java-Sprache und keine eigene Sprache.<\/p>\n<h4>Anweisungen und einfache Navigation in der JShell<\/h4>\n<p>In der JShell l\u00e4sst sich jeder Code schreiben, der im Rumpf einer Methode g\u00fcltig ist. Ein Semikolon am Ende einer Anweisung ist nicht n\u00f6tig:<\/p>\n<pre>jshell&gt; <strong>System.out.println( \"Hallo Welt\" )<\/strong>\n\n\"Hallo Welt\"\n\nCompilerfehler zeigt die JShell sofort an:\n\njshell&gt; <strong>System.out.pri()<\/strong>\n\n|\u00a0 Error:\n\n|\u00a0 cannot find symbol\n\n|\u00a0\u00a0\u00a0 symbol:\u00a0\u00a0 method pri()\n\n|\u00a0 System.out.pri()\n\n|\u00a0 ^------------^<\/pre>\n<p>Ausnahmen m\u00fcssen nicht behandelt werden, es lassen sich alle Methoden ohne try-catch aufrufen; falls es zu Ausnahmen kommt werden diese direkt gemeldet:<\/p>\n<pre>jshell&gt; <strong>Files.exists( Paths.get(\"c:\/\") )<\/strong>\n\n$2 ==&gt; true\n\n\n\n\njshell&gt; <strong>Files.exists( Paths.get(\"lala:\/\") )<\/strong>\n\n|\u00a0 java.nio.file.InvalidPathException thrown: Illegal char &lt;:&gt; at index 4: lala:\/\n\n|\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 at WindowsPathParser.normalize (WindowsPathParser.java:182)\n\n\u2026\n\n|\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 at (<strong>#3:1<\/strong>)<\/pre>\n<p>Die letzte Zeile zeigt die Zeilennummer im Skript an. Eine Liste der bisher eingegebenen Zeilen listet \/list auf, und das inklusive Zeilennummern. Diese sind n\u00fctzlich, wenn es zu Ausnahmen wie oben kommt.<\/p>\n<pre>jshell&gt; <strong>\/list<\/strong>\n\n\n\n\n\u00a0\u00a0 1 : System.out.println( \"Hallo Welt\" );\n\n\u00a0\u00a0 2 : Files.exists(Paths.get(\"c:\/\"))\n\n\u00a0\u00a0 3 : Files.exists(Paths.get(\"lala:\/\"))<\/pre>\n<p>Die JShell pflegt eine Historie der letzten Kommandos, die sich mit den Cursor-Tasten abrufen l\u00e4sst. Es funktioniert auch die Vervollst\u00e4ndigung mit der Tabulator-Taste wie in einer IDE, wobei die Gro\u00df-Kleinschreibung relevant ist:<\/p>\n<pre>jshell&gt; <strong>Sys<\/strong><strong>\u21b9<\/strong>\n\nSystem\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 SystemColor\u00a0\u00a0 SystemTray\n\n\n\n\njshell&gt; System<strong>.out.println(java.time.LocalDateTime.n<\/strong><strong>\u21b9<\/strong>\n\njshell&gt; System.out.println(java.time.LocalDateTime.n<strong>ow(<\/strong>\n\nnow(\n\n\n\n\njshell&gt; System.out.println(java.time.LocalDateTime.now(<strong>))<\/strong>\n\n2017-03-23T11:50:43.859385900<\/pre>\n<p>Mit dem Cursor l\u00e4sst sich in die Zeile vorher gehen und die Zeile nacheditieren.<\/p>\n<h4>Variablendeklarationen<\/h4>\n<p>Variablen lassen sich deklarieren und sp\u00e4ter jederzeit verwenden:<\/p>\n<pre>jshell&gt; <strong>String name = \"Christian\"<\/strong>\n\nname ==&gt; \"Christian\"<\/pre>\n<p>Die JShell gibt die Variable mit der Belegung zur Kontrolle aus.<\/p>\n<p>Variablen lassen sich mit einem ganz neuen Typ redefinieren:<\/p>\n<pre>jshell&gt; <strong>StringBuilder name = new StringBuilder( \"Christian\" )<\/strong>\n\nname ==&gt; Christian<\/pre>\n<p>Es lassen sich auch ohne Zuweisung Ausdr\u00fccke in die JShell setzen. Das Ergebnis des Ausdrucks wird einer tempor\u00e4ren Variablen zugewiesen, die standardm\u00e4\u00dfig mit einem Dollar beginnt und der einer Zahl folgt, etwa $1. Auf diese Variable l\u00e4sst sich sp\u00e4ter zugreifen:<\/p>\n<pre>jshell&gt; <strong>BigInteger.TEN.pow(10)<\/strong>\n\n$1 ==&gt; 10000000000\n\n\n\n\njshell&gt; <strong>$1<\/strong>\n\n$1 ==&gt; 10000000000\n\n\n\n\njshell&gt; <strong>$1.bitLength()<\/strong>\n\n$2 ==&gt; 34\n\n\n\n\njshell&gt; <strong>System.out.println(2*$2)<\/strong>\n\n68<\/pre>\n<p>Welche Variablen in welcher Reihenfolge in der Sitzung deklariert wurden zeigt das Kommando \/vars auf:<\/p>\n<pre>jshell&gt; <strong>\/<\/strong><strong>vars<\/strong>\n\n|\u00a0\u00a0\u00a0 StringBuilder name = Christian\n\n|\u00a0\u00a0\u00a0 BigInteger $1 = 10000000000\n\n|\u00a0\u00a0\u00a0 int $2 = 34<\/pre>\n<h4>Unvollst\u00e4ndige Eingabe<\/h4>\n<p>Wenn die JShell auf einen nicht kompletten Code trifft, symbolisiert die Ausgabe &#8230;&gt; die Notwendigkeit einer weitere Eingabe:<\/p>\n<pre>jshell&gt; <strong>System.out.println(<\/strong>\n\n\u00a0\u00a0 ...&gt; <strong>\"Hallo\"<\/strong>\n\n\u00a0\u00a0 ...&gt; <strong>+<\/strong>\n\n\u00a0\u00a0 ...&gt; <strong>\" Welt\"<\/strong>\n\n\u00a0\u00a0 ...&gt; <strong>)<\/strong>\n\nHallo Welt<\/pre>\n<h4>Import-Deklarationen<\/h4>\n<p>Standardm\u00e4\u00dfig sind f\u00fcr den Java-Compiler alle Typen vom Paket java.lang direkt importiert. Die JShell erweitert das um eine ganze Reihe weiterer Typen. Wir k\u00f6nnen sie mit dem Kommando \/imports erfragen:<\/p>\n<pre>jshell&gt; \/imports\n\n|\u00a0\u00a0\u00a0 import java.io.*\n\n|\u00a0\u00a0\u00a0 import java.math.*\n\n|\u00a0\u00a0\u00a0 import java.net.*\n\n|\u00a0\u00a0\u00a0 import java.nio.file.*\n\n|\u00a0\u00a0\u00a0 import java.util.*\n\n|\u00a0\u00a0\u00a0 import java.util.concurrent.*\n\n|\u00a0\u00a0\u00a0 import java.util.function.*\n\n|\u00a0\u00a0\u00a0 import java.util.prefs.*\n\n|\u00a0\u00a0\u00a0 import java.util.regex.*\n\n|\u00a0\u00a0\u00a0 import java.util.stream.*\n\n\n\n\njshell&gt; <strong>import java.awt.*<\/strong>\n\n\n\n\njshell&gt; <strong>\/imports<\/strong>\n\n|\u00a0\u00a0\u00a0 import java.io.*\n\n|\u00a0\u00a0\u00a0 import java.math.*\n\n|\u00a0\u00a0\u00a0 import java.net.*\n\n|\u00a0\u00a0\u00a0 import java.nio.file.*\n\n|\u00a0\u00a0\u00a0 import java.util.*\n\n|\u00a0\u00a0\u00a0 import java.util.concurrent.*\n\n|\u00a0\u00a0\u00a0 import java.util.function.*\n\n|\u00a0\u00a0\u00a0 import java.util.prefs.*\n\n|\u00a0\u00a0\u00a0 import java.util.regex.*\n\n|\u00a0\u00a0\u00a0 import java.util.stream.*\n\n|\u00a0\u00a0\u00a0 import java.awt.*<\/pre>\n<h4>Methoden- und Typ-Deklarationen<\/h4>\n<p>Methoden und Klassen lassen sich deklarieren und auch wieder \u00fcberschreiben, wenn eine neue Version eine alte ersetzen soll. JShell schreibt dann \u201emodified\u201c bzw. \u201ereplaced\u201c.<\/p>\n<pre>jshell&gt; <strong>String greet(String name) { return \"B\u00d6LK \" + name; }<\/strong>\n\n|\u00a0 created method greet(String)\n\n\n\n\njshell&gt; <strong>String greet(String name) { return \"Mit vorz\u00fcglicher Hochachtung \" + name; }<\/strong>\n\n|\u00a0 modified method greet(String)\n\n\n\n\njshell&gt; <strong>class MyFrame extends java.awt.Frame {}<\/strong>\n\n|\u00a0 created class MyFrame\n\n\n\n\njshell&gt; <strong>class MyFrame extends java.awt.Frame { MyFrame() { setTitle(\"FENSTER\"); } }<\/strong>\n\n|\u00a0 replaced class MyFrame\n\n\n\n\njshell&gt; new MyFrame().show()<\/pre>\n<p>Welche Methoden und neue Typen in der Sitzung deklariert sind listet \/methods und \/types auf:<\/p>\n<pre>jshell&gt; <strong>\/<\/strong><strong>methods<\/strong>\n\n|\u00a0\u00a0\u00a0 String greet(String)\n\n\n\n\njshell&gt; <strong>\/types<\/strong>\n\n|\u00a0\u00a0\u00a0 class OkButton\n\n|\u00a0\u00a0\u00a0 class MyFrame<\/pre>\n<p>Exceptions m\u00fcssen wie \u00fcblich behandelt werden, eine Sonderbehandlung, wie bei der direkten, in die JShell eingegeben Anweisungen, gibt es nicht.<\/p>\n<h4>Forward-Reference<\/h4>\n<p>Greift eine Methoden- oder Klassendeklaration auf Typen und Methoden zur\u00fcck, die in dem Kontext noch nicht vorhanden sind, ist das in Ordnung; allerdings m\u00fcssen alle Typen und Methoden sp\u00e4testens dann bekannt sein, wenn der Code ausgef\u00fchrt werden soll.<\/p>\n<pre>jshell&gt; <strong>double <\/strong><strong>cubic(double v) { return sqr(v) * v; }<\/strong>\n\n|\u00a0 created method cubic(double), however, it cannot be invoked until method sqr(double) is declared\n\n\n\n\njshell&gt; <strong>cubic(100)<\/strong>\n\n|\u00a0 attempted to call method cubic(double) which cannot be invoked until method sqr(double) is declared\n\n\n\n\njshell&gt; <strong>double sqr(double v) { return v*v; }<\/strong>\n\n|\u00a0 created method sqr(double)\n\n\n\n\njshell&gt; <strong>cubic(100)<\/strong>\n\n$14 ==&gt; 1000000.0<\/pre>\n<h4>Laden, speichern und ausf\u00fchren von Skripten<\/h4>\n<p>Snippets k\u00f6nnen in der JShell mit \/save Dateiname gespeichert, mit \/open Dateiname ge\u00f6ffnet und mit \/edit in einem Standard-Editor bearbeitet werden.<\/p>\n<p>Auf der Kommandozeile werden JShell-Skripte auf vorhandenen Skripten einfach ausgef\u00fchrt mit:<\/p>\n<pre>$ <strong>jshell datei<\/strong><\/pre>\n<h4>JShell API<\/h4>\n<p>Anders als die Benutzung von JavaScript aus Java heraus integriert sich die JShell nicht als Skript-Sprache. Stattdessen gibt es eine eigene API, in der die Klasse JShell im Mittelpunkt steht, wobei sich die M\u00f6glichkeiten der JShell-Kommandozeile eins zu eins in der API \u2013 dokumentiert unter <a href=\"http:\/\/download.java.net\/java\/jdk9\/docs\/jdk\/api\/jshell\/overview-summary.html\">http:\/\/download.java.net\/java\/jdk9\/docs\/jdk\/api\/jshell\/overview-summary.html<\/a> \u2013 wiederfinden lassen.<\/p>\n<p>Ein einfaches Beispiel:<\/p>\n<pre>try ( <strong>JShell shell = JShell.create()<\/strong> ) {\n\n\u00a0 \/\/ Semikolon wichtig!\n\n\u00a0 String program = \"java.math.BigInteger.TEN.pow( 10 );\";\n\n\u00a0 <strong>List&lt;SnippetEvent&gt; events =<\/strong> <strong>shell.eval( program )<\/strong>;\n\n\u00a0 for ( <strong>SnippetEvent snippetEvent : events<\/strong> ) {\n\n\u00a0\u00a0\u00a0 System.out.println( <strong>snippetEvent.status()<\/strong> );\n\n\u00a0\u00a0\u00a0 System.out.println( <strong>snippetEvent.value()<\/strong> );\n\n\u00a0\u00a0\u00a0 System.out.println( <strong>snippetEvent.snippet().source()<\/strong> );\n\n\u00a0\u00a0\u00a0 System.out.println( <strong>snippetEvent.snippet().kind()<\/strong> );\n\n\u00a0\u00a0\u00a0 if ( snippetEvent.snippet() instanceof VarSnippet ) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0 <strong>VarSnippet varSnippet = (VarSnippet) snippetEvent.snippet()<\/strong>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0 System.out.println( <strong>varSnippet.typeName()<\/strong> );\n\n\u00a0\u00a0\u00a0 }\n\n\u00a0 }\n\n}<\/pre>\n<p>Die Ausgabe ist:<\/p>\n<pre>VALID\n\n10000000000\n\njava.math.BigInteger.TEN.pow( 10 );\n\nVAR\n\njava.math.BigInteger<\/pre>\n<p>Ein paar Dinge sind an der API bemerkenswert, und zwar die Typen und Ergebnisse: sie sind Strings. varSnippet.typeName() ist ein String und snippetEvent.value() ebenso. Es ist nicht m\u00f6glich, eine echte Objektrepr\u00e4sentation zu bekommen, was die Nutzung als eingebettete Skriptsprache einschr\u00e4nkt.<\/p>\n<h4>Zum Weiterlesen<\/h4>\n<p>Weitere Informationen lassen sich aus dem <em>JEP 222: jshell: The Java Shell (Read-Eval-Print Loop)<\/em><a href=\"#_ftn1\" name=\"_ftnref1\">[1]<\/a> entnehmen und von den Quellen, die bei Java 9 dabei sind. Fragen \u00fcber das Produkt lassen sich in der Mailingliste http:\/\/mail.openjdk.java.net\/mailman\/listinfo\/kulla-dev stellen.<\/p>\n<p><a href=\"#_ftnref1\" name=\"_ftn1\">[1]<\/a>\u00a0\u00a0\u00a0\u00a0\u00a0 \u00a0\u00a0\u00a0\u00a0\u00a0 http:\/\/openjdk.java.net\/jeps\/222<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Im JDK 9 ist ein neues Programm eingezogen: die JShell. Mit ihr lassen sich auf einfache Weise kleine Java-Programme und einzelne Anweisungen testen, sogenannte Snippets, ohne eine gro\u00dfe IDE starten zu m\u00fcssen. Die JShell ist eine Befehlszeile (Shell), die nach dem Read-Evaluate-Print-Loop-Prinzip arbeitet: Read (Lesen): Eingabe des Programms von der Kommandozeile. Eine gute Shell bietet [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":"","_links_to":"","_links_to_target":""},"categories":[1,11,85],"tags":[],"class_list":["post-3800","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-insel","category-java-9"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3800","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/comments?post=3800"}],"version-history":[{"count":11,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3800\/revisions"}],"predecessor-version":[{"id":3811,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/posts\/3800\/revisions\/3811"}],"wp:attachment":[{"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/media?parent=3800"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/categories?post=3800"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.tutego.de\/blog\/javainsel\/wp-json\/wp\/v2\/tags?post=3800"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}