Développons en Java v 2.40 Copyright (C) 1999-2023 Jean-Michel DOUDOUX. |
|||||||
Niveau : | Fondamental |
JShell, signifiant Java Shell, est défini dans la JEP 222 : c'est une implémentation d'un outil en ligne de commande fourni dans le JDK à partir de Java 9 qui propose une console interactive de type REPL (Read-Eval-Print-Loop). JShell est issu des travaux du projet Kulla d'OpenJDK.
JShell permet d'écrire du code Java (expressions, instructions, déclaration, ...) qui est évalué individuellement sans avoir obligatoirement à les placer dans une classe ou une méthode.
Ce chapitre contient plusieurs sections :
Un REPL est un outil en ligne de commande qui permet rapidement d'écrire et d'exécuter du code. C'est un environnement de programmation en ligne de commande qui lit les instructions saisies, les évalue, affiche le résultat et ce de manière répétée.
Un outil de type REPL est une interface en ligne de commande qui permet :
De nombreux langages possèdent un outil de type REPL : Lisp, Python, Groovy, Ruby, ...
La plupart des langages s'exécutant dans une JVM ou non offre un outil de type RPEL (Scala, Groovy, Kotlin, Python, ...)
Un Shell Unix est aussi un outil de type REPL : il permet de lire des commandes, les évoluer, affiche le résultat et répéter ces traitements.
Un outil de type REPL peut avoir de nombreuses utilisations :
Il permet facilement d'explorer les fonctionnalités d'une API, ou de développer un petit prototype.
L'utilisation d'un outil de type REPL est particulièrement utile pour réduire les délais d'exécution. Un outil de type REPL permet de réduire le temps des boucles de retours notamment en permettant de voir le résultat du code saisi immédiatement. Cela augmente grandement la productivité.
Les IDE proposent généralement une solution pour permettre de tester du code Java sans avoir à écrire une méthode main() dans une classe.
Un outil de type REPL n'a pas pour but d'être un IDE : il ne propose donc pas d'interface graphique, de débogueur, ...
L'utilisation d'un outil de type REPL est orientée expression : c'est une approche assez différente de l'écriture de code Java qui est orientée déclaration.
Un REPL n'est pas adapté au développement de code complexe comme une application mais il est particulièrement efficace pour écrire de petites portions de code.
Java 9 offre un outil de type REPL (Read Evaluate Print Loop ou boucle de lecture, évaluation, impression en français) nommé JShell. Cette fonctionnalité a été développée dans le projet Kulla. JShell est intégré à Java 9 via la JEP 222 (JShell Java Enhancement Proposal) en tant qu'outil standard du JDK.
JShell est un outil en ligne de commande qui permet de saisir et d'évaluer dynamiquement des déclarations, des expressions et l'exécution de traitements sans avoir à les inclure dans une classe. C'est un outil interactif qui utilise l'API Compiler pour évaluer dynamiquement les expressions ou le code fourni : cela implique la compilation, l'exécution et le renvoie du résultat.
Il est possible de faire exécuter du code Java sans avoir à l'inclure dans une classe ou une méthode. Ceci permet de plus facilement et rapidement expérimenter du code Java.
JShell est un outil en ligne de commande qui permet d'évaluer des traitements, des déclarations et des expressions Java très facilement. Un outil de type REPL permet de saisir de manière interactive des fragments de code arbitraires, de les évaluer et d'afficher leurs résultats.
L'évaluation interactive du code saisi est plus efficace que le développement traditionnel en Java (écriture / compilation / exécution) qui requière généralement plusieurs étapes :
Ce workflow repose sur l'utilisation d'un ou plusieurs fichiers et d'un ou plusieurs outils. D'autant qu'une application Java dépend généralement de dizaines voire de centaines de bibliothèques et éventuellement de conteneurs notamment pour exécuter des applications d'entreprise utilisant Java EE.
Toutes ces étapes sont plutôt longues car elles reposent sur un ou plusieurs fichiers. De plus pour pouvoir être exécutée, la classe doit contenir une méthode public static void main() avec une signature particulière.
D'un autre côté, JShell propose une solution plus interactive. Le développeur peut rapidement saisir des portions de code qui vont être évaluées au fur et à mesure et JShell va fournir pour chacun un retour sur les résultats de l'évaluation.
Lors de la découverte d'un langage, le fait d'avoir un retour immédiat à chaque ligne de code saisie permet d'accélérer la courbe d'apprentissage. JShell peut être utilisé pour réduire la courbe d'apprentissage du langage Java ou de certaines API notamment en évitant le code inutile pour se concentrer sur le code utile.
La cible de JShell n'est pas que les étudiants qui se forment à Java mais aussi les développeurs qui pourront facilement explorer une API ou tester un algorithme.
JShell ne remplace pas un IDE mais il permet de tester facilement du code Java en ligne de commande interactive. Il est ainsi possible de tester du code sans avoir à écrire une classe avec une méthode main(). Avec JShell, les développeurs peuvent écrire du code, l'exécuter et continuer à faire évoluer leur code à la volée sans avoir à sortir pour une compilation et une exécution.
JShell est un outil qui peut donc changer la manière dont les développeurs vont apprendre et écrire du code pour tester des fonctionnalités. JShell peut offrir un réel intérêt dans certains cas notamment :
L'implémentation de JShell s'appuie de préférence sur des fonctionnalités existantes dans le JDK lorsque celles-ci sont disponibles, notamment :
JShell utilise aussi la bibliothèque jline2 pour gérer les saisies de la ligne de commande incluant les mises à jour des fragments et l'utilisation de l'historique.
Pour lancer JShell, il suffit de lancer la commande jshell fournie par un JDK 9 minimum dans le sous-répertoire bin. Le plus simple pour ne pas être obligé de préciser le chemin complet de la commande, il faut que la variable d'environnement JAVA_HOME pointe sur le répertoire d'installation du JDK de Java 9 que le sous-répertoire JAVA_HOME/bin soit ajouté dans le PATH du système. Une fois cela fait, il suffit d'exécuter la commande jshell.
Résultat : |
C:\java>java -version
java version "9.0.1"
Java(TM) SE Runtime Environment (build 9.0.1+11)
Java HotSpot(TM) 64-Bit Server VM (build 9.0.1+11, mixed mode)
C:\java>jshell
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
jshell>
Une fois démarré, JShell affiche une invite de commande « jshell> » par défaut qui permet de saisir un fragment de code ou une commande.
Comme proposé au lancement de JShell, il est possible de demander l'affichage de l'aide en ligne en utilisant la commande /help intro.
Exemple ( code Java 9 ) : |
jshell> /help intro
|
| intro
|
| The jshell tool allows you to execute Java code, getting immediate results.
| You can enter a Java definition (variable, method, class, etc), like: int x = 8
| or a Java expression, like: x + x
| or a Java statement or import.
| These little chunks of Java code are called 'snippets'.
|
| There are also jshell commands that allow you to understand and
| control what you are doing, like: /list
|
| For a list of commands: /help
Pour quitter JShell, il suffit d'utiliser la commande /exit.
Résultat : |
jshell> /exit
| Goodbye
C:\java>
Il est aussi possible d'utiliser le raccourci clavier CTRL+D.
Il est possible de démarrer JShell en mode verbeux en utilisant l'option -v à son lancement
Résultat : |
C:\java> jshell -v
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
jshell> 2+3
$1 ==> 5
| created scratch variable $1 : int
Lors de l'exécution en mode verbeux, JShell affiche non seulement le résultat de l'évaluation du fragment mais il fournit également une description de ce qu'il a fait, préfixée par un caractère « pipe » (une barre verticale).
Le mode verbeux est utile lors de l'apprentissage de l'utilisation de JShell. Avec de l'expérience, le mode normal ou un mode plus concis est préférable.
JShell propose des fonctionnalités pour faciliter la saisie de code et les interactions : un historique avec édition, complétion de code, ajout automatique des points virgules terminaux, imports, ...
Une session interactive en ligne de commande est ouverte accompagnée d'un message d'accueil indiquant quelle version de JShell est utilisée.
Traditionnellement, la première portion de code écrite et testée est un simple « Hello world ». Cela se fait très simplement avec JShell en saisissant l'instruction et en appuyant sur la touche Entrée
Résultat : |
jshell> System.out.println("Hello world")
Hello world
Plusieurs points sont notables :
Le point-virgule de fin d'instruction est optionnel : il n'est obligatoire que si elle est incluse dans un bloc de code ou si la ligne contient plusieurs instructions. Dans ces cas, chaque instruction doit obligatoirement se terminer par un point-virgule.
JShell propose aussi des surcharges des méthodes println() et printf() qui permettent de simplifier encore cet exemple, si ces fonctionnalités sont activées :
Résultat : |
jshell> printf("Hello world")
Hello world
Avec JShell, chaque ligne de code est saisie les unes après les autres avec un affichage immédiat du résultat de son exécution.
JShell maintient un état (state) qui contient le code évalué et le résultat de son exécution.
JShell facilite l'évaluation de code notamment en permettant :
Cela signifie, par exemple, qu'il est possible d'exécuter quelques lignes de Java sans avoir à écrire une classe ou une méthode.
JShell permet la saisie de deux types d'éléments :
JShell propose aussi de nombreuses fonctionnalités pour faciliter leur saisie :
Un fragment (snippet) correspond à un des éléments syntaxiques du Java Language Specification (JLS) :
Seuls les classes et les imports peuvent être gérés seuls : les autres types d'éléments sont associés à un contexte créé par JShell :
Pour permettre d'explorer et de tester du code, la déclaration d'un élément doit être capable d'évoluer dans le temps. Dans une session JShell, chaque élément possède une clé :
A un instant donné, un élément ne peut avoir qu'une seule déclaration.
L'état de la session est maintenu cohérent au fur et à mesure que les fragments sont évalués : chaque changement qui implique un impact est propagé suite à l'évaluation.
Le résultat d'une évaluation correcte d'un fragment peut prendre trois valeurs :
Si le résultat de l'évaluation est modified ou replaced, alors la version existante du fragment est retirée de la session et la nouvelle version la remplace.
Quant un fragment est ajouté, il peut créer des références non résolues, car elle n'existe pas encore.
Quant un fragment est remplacé, il peut mettre à jour un ou plusieurs fragments qui en font usage. C'est par exemple le cas si le type de la valeur de retour change : cela peut éventuellement rendre les éléments impactés invalides.
Lorsqu'une variable est remplacée, par une action directe ou indirecte, sa valeur est réinitialisée avec sa valeur par défaut selon ton type.
Lorsqu'un élément est invalide, soit à cause d'une référence à venir, soit parce qu'elle devient invalide à la suite d'une mise à jour, la déclaration est «corralled». Une déclaration corrigée peut être utilisée dans le code d'autres déclarations. Par contre, une exception sera levée à l'exécution tant que code n'est pas corrigé pour le rendre valide.
Le texte saisi qui n'est pas une commande est désigné par le terme fragment (snippet).
JShell permet la déclaration de différents éléments du langage Java :
Tous ces éléments sont désignés sous le terme fragment (snippet).
Il n'est pas nécessaire de créer une classe, ni de méthode main() : il suffit de saisir un fragment de code Java et d'appuyer sur la touche entrée pour que celui-ci soit évalué et le résultat affiché.
Si le fragment tient sur une seule ligne, il est immédiatement évalué et le résultat est affiché. Ce résultat peut être une erreur.
Résultat : |
jshell> int i = 10;
i ==> 10
Si une expression est saisie, le résultat de son évaluation est affecté à une variable (scratched variable) créée pour l'occasion. Son nom commence par un $ suivi de l'identifiant du fragment.
Résultat : |
jshell> 4+6
$2 ==> 10
Comme JShell permet la saisie des fragments un par un, le point-virgule de fin d'instruction est optionnel : s'il n'est pas présent, il sera automatiquement ajouté avant l'évaluation.
Si plusieurs instructions sont saisies en même temps, le ou les points virgules de séparation sont toujours obligatoire.
Résultat : |
jshell> int i = 1; int j = 2;
i ==> 1
j ==> 2
Un fragment peut être une méthode, un type (classe, interface, énumération, record) : tous ces éléments requièrent généralement plusieurs lignes donc un fragment n'est pas obligatoirement sur une seule ligne.
Il est donc possible de saisir un fragment composé de plusieurs lignes, comme par exemple la définition d'une méthode. Le prompt change pour devenir ...> pour indiquer la saisie de la suite du fragment.
Résultat : |
jshell> void afficher(String message) {
...> System.out.println(message);
...> }
Pour exécuter la méthode, il suffit de l'invoquer en tant que fragment.
Résultat : |
jshell> afficher("bonjour");
bonjour
Pour modifier la définition d'un snippet (variable, méthode, classe, ...), il suffit de ressaisir sa définition. Attention, parfois la redéfinition peut induire des incompatibilités comme par exemple si une variable est redéfinie avec un autre type.
Les variables, les méthodes et les classes définies ne sont pas encapsulées dans une classe accessible par l'utilisateur de JShell mais sont encapsulées comme des variables statiques d'une classe générée en interne.
Toutes les variables, les méthodes et les types définies sont accessibles durant toute la session courante de JShell.
JShell permet la déclaration implicite et explicite de variables. Il est possible de saisir directement une expression Java comme par exemple une opération arithmétique, une manipulation de chaînes de caractères, la définition de variables, la création d'une instance, l'invocation d'une méthode, ...
Une fois l'expression saisie et la touche « Entrée » appuyée, l'expression est immédiatement évaluée et le résultat de cette évaluation est affiché.
Si ce n'est pas fait explicitement dans l'expression, le résultat est affecté à une variable temporaire (scratch variable) dont le nom est composé du signe dollar suivi d'un nombre unique qui correspond à l'identifiant du fragment.
Résultat : |
jshell> 1+2
$1 ==> 3
Cette nouvelle variable implicitement définie peut elle-même être réutilisée dans une autre expression.
Résultat : |
jshell> $1+2
$2 ==> 5
Il est bien sûr possible de définir explicitement le nom de la variable dans l'expression
Résultat : |
jshell> int nbElements = 10
nbElements ==> 10
Il est possible de définir une variable sans l'initialiser.
Résultat : |
jshell> Date date
date ==> null
jshell> int i
i ==> 0
L'expression peut être l'invocation d'une méthode
Résultat : |
jshell> System.out.println($1)
3
L'expression peut concerner la définition et la manipulation de chaînes de caractères
Résultat : |
jshell> String salutation = "Hello " + "world"
salutation ==> "Hello world"
jshell> salutation.toUpperCase()
$6 ==> "HELLO WORLD"
JShell affiche une erreur si la syntaxe est incorrecte.
Résultat : |
jshell> salutation.toUppercase()
| Error:
| cannot find symbol
| symbol: method toUppercase()
| salutation.toUppercase()
| ^--------------------^
L'expression peut être l'invocation d'une méthode : de la même manière le résultat est implicitement affecté à une variable temporaire par défaut.
Résultat : |
jshell> salutation.split(" ")
$7 ==> String[2] { "Hello", "world" }
Une expression peut être saisie sur plusieurs lignes. Si JShell détecte que l'expression n'est pas complète, un prompt différent est affiché pour indiquer de saisir la suite de l'expression.
Résultat : |
jshell> int a =
...> 10
a ==> 10
La définition d'une variable avec le modificateur static ou final affiche un warning : la variable est définie mais le modificateur est ignoré.
Exemple ( code Java 9 ) : |
jshell> static int a = 10;
| Warning:
| Modifier 'static' not permitted in top-level declarations, ignored
| static int a = 10;
| ^----^
a ==> 10
jshell> final int a = 10;
| Warning:
| Modifier 'final' not permitted in top-level declarations, ignored
| final int a = 10;
| ^---^
a ==> 10
Les snippets peuvent être des instructions de contrôle de flux (if, for, while, ...). Il est possible d'écrire ces instructions sur plusieurs lignes.
JShell est suffisamment intelligent pour reconnaître les instructions multilignes et propose une invite avec un symbole ...> pour nous permettre de saisir la suite de l'instruction sur la ligne suivante.
Résultat : |
jshell> if (note >=12) {
...> System.out.println("bien");
...> } else {
...> System.out.println("faible");
...> }
bien
Il est à noter que dans ce cas les instructions doivent se terminer par un point-virgule.
Résultat : |
jshell> if (note >=12) {
...> System.out.println("bien")
...> } else {
...> System.out.println("faible")
...> }
| Error:
| ';' expected
| System.out.println("bien")
| ^
| Error:
| ';' expected
| System.out.println("faible")
|
L'expression peut évidemment être une boucle.
Résultat : |
jshell> String[] lettres = {"A", "B", "C", "D"}
lettres ==> String[4] { "A", "B", "C", "D" }
jshell> for (String lettre : lettres) {
...> System.out.print(lettre + " ");
...> }
A B C D
Les instructions break et continue ne sont pas utilisables comme instruction de premier niveau.
Exemple ( code Java 9 ) : |
jshell> continue;
| Error:
| continue outside of loop
| continue;
| ^-------^
jshell> break;
| Error:
| break outside switch or loop
| break;
| ^----^
Il est possible de définir une méthode directement sans avoir à l'inclure dans une classe en préambule. Il suffit de saisir la signature et le corps de la méthode.
Résultat : |
jshell> long ajouter(int a, int b) {
...> return a + b;
...> }
| created method ajouter(int,int)
Attention, les points-virgules de terminaison sont obligatoires dans les blocs de code.
Une fois la méthode définie, il est possible de l'invoquer à n'importe quel moment tant que la session n'est pas fermée.
Résultat : |
jshell> ajouter(2, 3)
$13 ==> 5
Il est possible de remplacer la définition d'une méthode simplement en saisissant son code.
Résultat : |
jshell> void saluer() {
...> System.out.println("Bonjour");
...> }
| created method saluer()
jshell> saluer()
Bonjour
jshell> void saluer() {
...> System.out.println("Hello");
...> }
| modified method saluer()
jshell> saluer()
Hello
La création d'une méthode statique n'est pas possible : le mot clé static est ignoré.
Résultat : |
jshell> public static void afficher(String msg) { System.out.println(msg); }
| Warning:
| Modifier 'static' not permitted in top-level declarations, ignored
| public static void afficher(String msg) { System.out.println(msg); }
| ^-----------^
| created method afficher(String)
Il est possible de définir une classe abstraite.
Exemple ( code Java 9 ) : |
jshell> public abstract class MaClasseAbstraite {}
| created class MaClasseAbstraite
Il n'est pas possible d'utiliser le modificateur synchronized.
Exemple ( code Java 9 ) : |
jshell> synchronized void afficher() {}
| Error:
| Modifier 'synchronized' not permitted in top-level declarations
| synchronized void afficher() {}
| ^----------^
JShell permet la création de classes, d'interfaces, d'énumérations et de records. Il suffit de saisir l'ensemble des lignes de code de la classe. Une fois l'intégralité du code saisi, JShell évalue le code saisi et affiche le résultat de cette évaluation.
Résultat : |
jshell> class MaClasse {
...> public void afficher(String message) {
...> System.out.println(message);
...> }
...> }
| created class MaClasse
La création d'une classe peut être fastidieuse : une erreur de saisie n'est détectée que lors de l'évaluation de l'intégralité du code saisi.
Pour faciliter la saisie ou la modification d'une classe, il est plus simple d'utiliser la commande /edit.
Il est aussi possible de charger et d'évaluer le code d'une classe en utilisant la commande /open.
Une fois définie, il est possible d'utiliser la classe.
Résultat : |
jshell> MaClasse mc = new MaClasse()
mc ==> MaClasse@370736d9
jshell> mc.afficher("Bonjour");
Bonjour
Il est aussi possible de faire un copier/coller du code de la classe.
Résultat : |
jshell> class Personne { private String nom; private String prenom;
public Personne(String nom, String prenom) { super(); this.nom = nom; this.prenom
= prenom; } public Personne() { super(); } public String getNom() { return
nom ; } public void setNom(String nom) { this.nom = nom; } public String
getPrenom() { return prenom; } public void setPrenom(String prenom) {
this.prenom = prenom; } }
| created class Personne
Il n'est pas possible de déclarer des packages : tout le code saisi et exécuté dans JShell est placé dans un package transitoire interne.
Résultat : |
jshell> package mon.package;
| Error:
| illegal start of expression
| package mon.package;
| ^
Il n'est pas possible d'utiliser directement this dans les fragments
Résultat : |
jshell> System.out.println(this)
| Error:
| non-static variable this cannot be referenced from a static context
| System.out.println(this)
| ^--^
JShell propose plusieurs commandes pour :
Toutes les commandes commencent par un caractère « / », ce qui les distinguent des fragments qui eux seront évalués : elles sont de la forme /xxx où xxx est le nom de la commande.
L'utilisation d'un REPL est différente de celle d'un IDE. Comme les fragments sont évalués au fur et à mesure, plusieurs commandes permettent d'obtenir la liste des éléments saisis dans la session courante : variables, méthodes, types.
JShell propose de nombreuses commandes :
Commande |
Rôle |
/vars |
Afficher la liste des variables définies dans la session courante /vars [<name or id>|-all|-start] |
/methods |
Afficher la liste des méthodes définies dans la session courante /methods [<name or id>|-all|-start] |
/list |
Afficher la liste des fragments saisis de la session courante /list [<name or id>|-all|-start] |
/imports |
Afficher la liste des imports définis dans la session courante /imports |
/types |
Afficher la liste des types définis dans la session courante /types [<name or id>|-all|-start] |
/edit |
Editer un fragment précisé par son nom ou son id /edit <name or id> |
/exit |
Fermer la session courante et arrêter JShell /exit |
/drop |
Supprimer un fragment précisé par son nom ou son id /drop <name or id> |
/open |
Ouvrir un fichier dont le contenu est ajouté dans la session /open <file> |
/save |
Enregistrer les éléments de la session dans un fichier /save [-all|-history|-start] <file> |
/env |
Configurer le context d'évaluation (classpath, modulepath) /env [-class-path <path>] [-module-path <path>] [-add-modules <modules>] ... |
/reset |
Réinitialiser la session /reset [-class-path <path>] [-module-path <path>] [-add-modules <modules>]... |
/history |
Afficher l'historique des fragments et commandes saisis /history |
/help |
Afficher l'aide en ligne /help [<command>|<subject>] |
/set |
Configurer l'outils /set editor|start|feedback|mode|prompt|truncation|format ... |
/reload |
Recharger la session : effectue un reset et réexécute les fragments de la session /reload [-restore] [-quiet] [-class-path <path>] [-module-path <path>]... |
Il est possible de ne saisir que les premiers caractères de la commande tant que cela n'induit pas d'ambiguďtés.
Résultat : |
jshell> /ex
|
Goodbye
Si une ambiguďté est détectée alors un message d'erreur est affiché.
Résultat : |
jshell> /e
| Command: '/e' is ambiguous: /edit, /exit, /env
| Type /help for help.
La commande /help ou /? permet d'obtenir l'aide en ligne proposant notamment la liste des commandes utilisables dans JShell.
Résultat : |
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 referenced by name or id
| /drop <name or id>
| delete a source entry referenced by name or id
| /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
...
Il est possible d'obtenir l'aide en ligne d'une commande en utilisant la commande /help suivi du nom de la commande.
Résultat : |
jshell> /help set
|
| /set
| ====
|
| Set the jshell tool configuration information, including:
| the external editor to use, the startup definitions to use, a new feedback mode,
| the command prompt, the feedback mode to use, or the format of output.
|
...
Il est aussi possible d'obtenir l'aide en ligne d'une option d'une commande en utilisant la commande /help suivi du nom de la commande puis de l'option.
Résultat : |
jshell> /help set start
|
| /set start
| ==========
|
| Set the startup configuration -- a sequence of snippets and commands read at startup:
|
| /set start [-retain] <file>...
|
| /set start [-retain] -default
|
| /set start [-retain] -none
...
La commande /exit permet de quitter JShell
Résultat : |
C:\Program Files\Java\jdk-9\bin>jshell
| Welcome to JShell -- Version 9-ea
| For an introduction type: /help intro
jshell> /exit
| Goodbye
C:\Program Files\Java\jdk-9\bin>
La commande /list affiche la liste des fragments saisis.
Résultat : |
jshell> /list
1 : 1+2
2 : $1+3
3 : System.out.println($2)
4 : void afficher(String m) {
System.out.println(m);
}
5 : afficher("bonjour")
Chacun des fragments est préfixé par son identifiant.
L'option -start affiche les fragments des scripts de démarrage. Leur identifiant commence par une lettre « s ».
Résultat : |
jshell> /list -start
s1 : import java.io.*;
s2 : import java.math.*;
s3 : import java.net.*;
s4 : import java.nio.file.*;
s5 : import java.util.*;
s6 : import java.util.concurrent.*;
s7 : import java.util.function.*;
s8 : import java.util.prefs.*;
s9 : import java.util.regex.*;
s10 : import java.util.stream.*;
L'option -all de la commande /list affiche les fragments des scripts de démarrage et tous les fragments saisis. Par défaut, la commande /list n'affiche pas les fragments dont l'évaluation a échouée.
En utilisant l'option -all, il est possible d'obtenir l'intégralité des fragments saisis, indépendamment du résultat de leur évaluation, ainsi que les fragments exécutés par les scripts de démarrage.
L'affichage de chaque identifiant de chaque fragment dépend du type d'éléments :
Résultat : |
jshell> /list -all
s1 : import java.io.*;
s2 : import java.math.*;
s3 : import java.net.*;
s4 : import java.nio.file.*;
s5 : import java.util.*;
s6 : import java.util.concurrent.*;
s7 : import java.util.function.*;
s8 : import java.util.prefs.*;
s9 : import java.util.regex.*;
s10 : import java.util.stream.*;
1 : 1+2
2 : $1+3
3 : System.out.println($2)
4 : void afficher(String m) {
System.out.println(m);
}
5 : afficher("bonjour")
e1 : xxx
La commande /list n'affiche que les fragments : pour obtenir la liste de fragments et des commandes saisis il faut utiliser la commande /history.
La commande /history affiche tous les fragments et toutes les commandes saisis dans la session courante, dans leur ordre de saisie.
Résultat : |
jshell> /history
/list -start
1+2
$1+3
System.out.println($2)
void afficher(String m) {
System.out.println(m);
}
afficher("bonjour")
/list
/list -all
/history
Par défaut, JShell permet d'utiliser les imports des principaux packages.
La commande /imports permet d'afficher la liste des imports utilisables dans l'environnement.
Résultat : |
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.*
Il est possible d'ajouter un import dans l'environnement simplement en saisissant une expression import.
Résultat : |
jshell> import java.time.*
Elle permet d'obtenir la liste de toutes les variables définies dans la session courante ainsi que leur valeur.
Exemple ( code Java 9 ) : |
jshell> /vars
| int $2 = 3
| int $3 = 5
| int nbElements = 10
| String salutation = "Hello world"
| String $6 = "HELLO WORLD"
| String[] $7 = String[2] { "Hello", "world" }
| int note = 12
| String[] lettres = String[4] { "A", "B", "C", "D" }
| long $13 = 5
| MaClasse mc = MaClasse@370736d9
Il est possible de n'afficher que la variable dont l'identifiant suit la commande
Résultat : |
jshell> /vars 1
| int $1 = 3
Il est possible de n'afficher que la variable dont le nom suit la commande.
Résultat : |
jshell> /vars i
| int i = 10
L'option -all de la commande /vars affiche toutes les variables actives mais aussi celles dont la définition a échoué ou sont supprimées.
Résultat : |
jshell> /vars -all
| int $1 = (not-active)
| help vars = (not-active)
| int i = 10
L'option -start de la commande /vars affiche toutes les variables définies par les scripts de démarrage.
Elle permet d'obtenir la liste des méthodes définies dans la session courante. Elle affiche le type de retour, le nom et les types des paramètres entre parenthèses de chaque méthode active de la session.
Résultat : |
jshell> /methods
| void afficher(String)
| long ajouter(int,int)
Il est possible de n'afficher que la méthode dont le nom suit la commande.
Résultat : |
jshell> /methods afficher
| void afficher(String)
L'option -all de la commande /methods affiche toutes les méthodes actives mais aussi celles dont la définition a échoué ou sont supprimées.
L'option -start de la commande /methods affiche toutes les méthodes définies par les scripts de démarrage.
Elle permet d'obtenir la liste des classes, interfaces, énumérations et records définies dans la session courante.
Résultat : |
jshell> /types
| class Personne
| class MaClasse
Il est possible de n'afficher que le type dont le nom suit la commande.
Résultat : |
jshell> /types Personne
| class Personne
L'option -all de la commande /types affiche tous les types actifs mais aussi ceux dont la définition a échoué ou sont supprimés.
L'option -start de la commande /types affiche toutes les méthodes définies par les scripts de démarrage.
Elle permet d'enregistrer les expressions saisies dans la session courante dans le fichier dont le nom suit la commande.
Résultat : |
jshell> /save moncode.jsh
Le fichier créé n'est pas un fichier Java : il n'est pas possible de le compiler avec le compilateur javac.
Résultat : |
C:\java>type moncode.jsh
1+2
"bonjour"
System.out.println($1 + " "+$2)
int i = 5;
long additionner(int a, int b) {
return (long) a + b;
}
additionner(1,2)
$2.toUpperCase()
Il est possible de modifier le fichier en utilisant un éditeur tout en prenant garde de le conserver valide.
Le fichier pourra être rechargé dans JShell en utilisant la commande /open.
Il est possible de limiter les fragments sauvegardés à ceux précisé avant le nom du fichier
Résultat : |
jshell> /save 2-4 monfichier.jsh
L'option -all de la commande /save enregistre tous les fragments saisis même ceux dont la définition a échoué ou sont supprimés ainsi que les éléments des scripts de démarrage.
L'option -history de la commande /save enregistre tous les fragments et les commandes saisis.
L'option -start de la commande /save enregistre les éléments des scripts de démarrage dans un fichier.
Elle permet de lire les expressions contenues dans le fichier dont le nom suit la commande pour les ajouter dans la session en les évaluant. Le résultat de cette évaluation peut échouer et conduire à une erreur.
Résultat : |
jshell> /open moncode.jsh
La commande /open peut aussi utiliser pour charger un fichier .java contenant la définition d'une classe.
Exemple ( code Java 9 ) : |
jshell> /types
jshell> /open Hello.java
jshell> /types
| class Hello
JShell propose trois scripts prédéfinis qu'il est possible de charger avec la commande /open :
Résultat : |
jshell> /open PRINTING
jshell> println("coucou")
coucou
JShell permet d'éditer un nouveau fragment ou un fragment précédemment saisi pour le modifier dans un éditeur basic fournit par JShell ou par l'éditeur externe de son choix. L'édition est alors plus simple notamment si le fragment est composé de plusieurs lignes.
Par défaut, une boîte de dialogue Swing est affichée pour permettre de saisir le nouveau code du fragment : c'est une simple boîte de dialogue qui permet la saisie du code du fragment en multilignes.
La commande /edit peut être utilisée sur un fragment valide ou invalide. Elle est particulièrement utile pour éditer un fragment contenu sur plusieurs lignes et notamment pour en corriger un dont l'évaluation a échoué dans avoir à ressaisir son contenu intégral.
Si la session ne contient aucun fragment, alors pour créer une nouvelle portion de code à partir de l'éditeur, il suffit d'invoquer la commande /edit sans argument.
Résultat : |
jshell> /reset
| Resetting state.
jshell> /edit
L'éditeur de texte se lance avec un contenu vide puisque l'état de la session a été réinitialisée. Il est alors possible de saisir les lignes de code du fragment dans l'éditeur.
Lorsque l'on clique sur « Accept », la méthode afficher() est ajoutée et elle est exécutée avec le paramètre bonjour.
Résultat : |
jshell> /edit
| created method afficher(String)
bonjour
Pour valider les modifications, il suffit de cliquer sur le bouton « Accept » puis sur le bouton « Exit » pour fermer la boîte de dialogue.
Lorsque l'on quitte l'éditeur, JShell affiche un message si l'élément est modifié ou remplacé.
Résultat : |
jshell> /edit afficher
| modified method afficher(String)
Cela fonctionne aussi si la session contient déjà des fragments en invoquant la commande /edit : dans ce cas, l'éditeur s'ouvre avec l'intégralité des fragments.
Lors du clique sur « Accept », la nouvelle méthode est ajoutée et exécutée : les autres fragments restants inchangés, ils ne sont pas exécutés. Les fragments existants sont modifiés ou remplacés et les nouveaux fragments sont ajoutés.
Résultat : |
jshell> /edit
| created method ajouter(int,int)
$4 ==> 5
Remarque : tant que l'éditeur n'est pas fermé, il n'est pas possible de saisir de fragments ou de commande dans JShell.
Il est possible de préciser, en paramètre de la commande /edit, l'identifiant du fragment à modifier.
Exemple ( code Java 9 ) : |
jshell> /list
1 : int valeur = 10;
2 : String message = "Bonjour";
3 : void afficher(String message) {
System.out.println(message);
}
jshell> /edit 3
Il est aussi possible d'indiquer le nom de l'élément à modifier à la commande /edit.
Exemple ( code Java 9 ) : |
jshell> /edit afficher
Il est possible de passer en paramètre de la commande /edit plusieurs ID des éléments à éditer.
Exemple ( code Java 9 ) : |
jshell> /list
1 : public void afficher() {}
3 : public void afficher(String msg) { System.out.println(" " + msg); }
4 : int i = 0;
5 : String msg = "";
jshell> /edit 3 4
En cours d'édition du code, il est possible de cliquer sur le bouton « Accept » : JShell va réévaluer le code en cours d'édition sans fermer l'éditeur. Cela permet facilement de valider le code saisi sans avoir à quitter l'éditeur. Si l'évaluation du code se fait sans erreur alors un nouvel ID est affecté à la portion de code.
Exemple ( code Java 9 ) : |
jshell> /edit
| created method afficher()
| created method afficher(String)
jshell> /list
1 : public void afficher() {}
2 : public void afficher(String msg) { System.out.println(msg); }
Il est possible de configurer un éditeur de texte externe en utilisant la commande /set avec l'option editor.
La commande /drop permet de supprimer un ou plusieurs snippets précédemment saisis.
Les snippets concernés peuvent être précisés en utilisant un identifiant, une plage d'identifiants, un nom ou une combinaison de ces éléments.
Exemple ( code Java 9 ) : |
jshell> /list
1 : public void afficher() {}
3 : public void afficher(String msg) { System.out.println(" " + msg); }
5 : String msg = "";
6 : int i = 10;
jshell> /drop 5
| dropped variable msg
jshell> /list
1 : public void afficher() {}
3 : public void afficher(String msg) { System.out.println(" " + msg); }
6 : int i = 10;
jshell> /drop i
| dropped variable i
jshell> /list
1 : public void afficher() {}
3 : public void afficher(String msg) { System.out.println(" " + msg); }
Plusieurs commandes permettent de réexécuter un fragment précédemment exécuté.
Commande |
Rôle |
/<id> |
Réexécuter le fragment dont l'identifiant est précisé |
/! |
Réexécuter le dernier fragment |
/-<n> |
Réexécuter le n-ième fragment précédent |
Exemple ( code Java 9 ) : |
jshell> /reset
| Resetting state.
jshell> println("1")
1
jshell> println("2")
2
jshell> println("3")
3
jshell> /list
1 : println("1")
2 : println("2")
3 : println("3")
jshell> /1
println("1")
1
jshell> /list
1 : print("1")
2 : print("2")
3 : print("3")
4 : print("1")
jshell> /-2
print("3")
3
jshell> /!
print("3")
3
Elle permet de configurer l'environnement de JShell.
L'option editor permet de configurer l'éditeur de texte à utiliser par la commande /edit. Elle possède plusieurs options qui peuvent se combiner :
/set editor [-retain] [-wait] <command>
/set editor [-retain] -default
/set editor [-retain] -delete
L'option -retain permet de rendre la configuration permanente. Ainsi, celle-ci sera utilisée dans les prochaines sessions ouvertes.
L'argument <command> permet de préciser la commande du système d'exportation à exécuter pour lancer l'éditeur externe. Des arguments peuvent être ajoutés après la commande en les séparant par des espaces. Lors de l'utilisation d'un éditeur externe, un fichier temporaire est créé et ce dernier est fourni en dernier argument de la commande.
Résultat : |
jshell> /set editor notepad
| Editor set to: notepad
L'option -default permet de demander l'utilisation de l'éditeur de texte fourni par défaut dans JShell.
L'option -delete permet de supprimer la configuration actuelle et de revenir à la configuration précédente.
L'option -wait permet de demander au développeur d'indiquer lorsque le mode d'édition doit se terminer. Cette option peut être utile lorsque l'éditeur utilisé ne bloque pas JShell lors de son utilisation.
Pour connaître la configuration de l'éditeur actuelle, il suffit simplement d'utiliser la commande /set editor
Résultat : |
jshell> /set editor
| /set editor -default
L'option start permet de configurer un ensemble de fragments ou de commandes exécuté au démarrage d'une session. Des fragments et commandes précisés via cette option seront exécutés lors de la l'invocation des commandes /reset, /reload et /env.
La commande /set start possède plusieurs paramètres qui peuvent se combiner :
/set start [-retain] <file>...
/set start [-retain] -default
/set start [-retain] -none
L'option -retain permet de rendre la configuration permanente : celle-ci sera utilisée dans les prochaines sessions de JShell.
L'option -default permet de demander la configuration de démarrage par défaut : celle-ci ne concerne que l'import des quelques packages par défaut.
L'option -none permet de demander qu'aucune configuration de démarrage ne soit utilisée.
L'argument <file> peut être un fichier qui contient les fragments des commandes ou une des trois valeurs prédéfinies dans JShell :
Rôle |
|
DEFAULT |
Importer les packages par défaut |
PRINTING |
Définir de méthodes pour faciliter l'affichage de données |
JAVASE |
Importer tous les packages de Java SE |
Il est possible de préciser plusieurs fichiers ou valeur prédéfinies
Résultat : |
jshell> /set start -retain DEFAULT PRINTING
Pour afficher la configuration de démarrage, il suffit d'utiliser la commande /set start
Résultat : |
jshell> /set start
| /set start -retain DEFAULT PRINTING
| ---- DEFAULT ----
| 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.*;
| ---- PRINTING ----
| void print(boolean b) { System.out.print(b); }
| void print(char c) { System.out.print(c); }
| void print(int i) { System.out.print(i); }
...
JShell propose une fonctionnalité qui nous permet de lui demander de terminer la commande saisie en tapant le début de la commande puis en appuyant sur la touche TAB.
Par exemple, il suffit de saisir un caractère slash puis d'appuyer sur la touche tabulation pour obtenir la liste des commandes utilisables.
Résultat : |
jshell> /
/! /? /drop /edit /env /exit /help /history /imports
/list /methods /open /reload /reset /save /set /types /vars
<press tab again to see synopsis>
jshell> /
Pour obtenir une liste détaillée, il suffit d'appuyer de nouveau sur la touche tabulation comme proposée à la fin de la liste des commandes.
Résultat : |
jshell> /
/! /? /drop /edit /env /exit /help /history /imports
/list /methods /open /reload /reset /save /set /types /vars
<press tab again to see synopsis>
jshell> /
/!
re-run last snippet
/-<n>
re-run n-th previous snippet
/<id>
re-run snippet by id
/?
get information about jshell
/drop
delete a source entry referenced by name or id
/edit
edit a source entry referenced by name or id
/env
view or change the evaluation context
/exit
exit jshell
/help
get information about jshell
/history
history of what you have typed
/imports
list the imported items
/list
list the source you have typed
/methods
list the declared methods and their signatures
/open
open a file as source input
/reload
reset and replay relevant history -- current or previous (-restore)
/reset
reset jshell
/save
Save snippet source to a file.
/set
set jshell configuration information
/types
list the declared types
/vars
list the declared variables and their values
<press tab again to see full documentation>
jshell> /
Il est possible de saisir le début de la commande et d'appuyer sur la touche TAB.
Résultat : |
jshell> /l
Si JShell peut terminer la saisie du code avec une unique solution, alors la commande est terminée.
Résultat : |
jshell> /list
Si JShell ne peut pas terminer la saisie du code avec une unique solution, alors les différentes possibilités sont affichées.
Résultat : |
jshell> /re
/reload /reset
<press tab again to see synopsis>
Pour obtenir plus d'informations sur les commandes affichées, il suffit d'appuyer à nouveau sur la touche TAB.
Résultat : |
jshell> /re
/reload
reset and replay relevant history -- current or previous (-restore)
/reset
reset jshell
<press tab again to see full documentation>
En pressant de nouveau la touche TAB, une information détaillée est affichée.
Résultat : |
jshell> /re
/reload
Reset the jshell tool code and execution state then replay each valid snippet
and any /drop commands in the order they were entered.
/reload
Reset and replay the valid history since jshell was entered, or
a /reset, or /reload command was executed -- whichever is most
recent.
/reload -restore
Reset and replay the valid history between the previous and most
recent time that jshell was entered, or a /reset, or /reload
command was executed. This can thus be used to restore a previous
jshell tool session.
/reload [-restore] -quiet
With the '-quiet' argument the replay is not shown. Errors will display.
Each of the above accepts context options, see:
/help context
<press tab again to see next page>
L'utilisation de la touche TAB fonctionne aussi pour les options des commandes
Résultat : |
jshell> /list -
-all -history -start
<press tab again to see synopsis>
jshell> /list -
Comme pour le nom d'une commande, l'appui une seconde fois sur la touche TAB affiche une description des options.
Si les premières lettres de l'option suffisent pour être terminé, le nom de l'option est complété.
Lors de la saisie de commande qui requière un nom de fragment, il est aussi possible d'utiliser la touche TAB pour compléter le nom d'un élément précédemment créé dans la session JShell.
Résultat : |
jshell> /list
jshell> int valeur = 10;
valeur ==> 10
jshell> /ed v
L'appui sur la touche TAB complète le nom du fragment
Résultat : |
jshell> /ed valeur
La touche TAB peut aussi être utilisée pour compléter le nom d'un fichier requis par une commande.
Résultat : |
jshell> /open
Open a file and read its contents as snippets and commands.
/open <file>
Read the specified file as jshell input.
Un appui de nouveau sur la touche TAB affiche la liste des fichiers présents dans le répertoire courant.
Résultat : |
jshell> /open
C:\ Hello.java
test.jsh test_java9/
<press tab again to see synopsis>
jshell> /open
La touche TAB peut aussi être utilisée pour compléter le nom partiel d'un fichier.
Résultat : |
jshell> /open test
test.jsh test_java9/
jshell> /open test.jsh
Il est possible de réduire la quantité de caractères à saisir en utilisant les abréviations de commandes. Cette abréviation consiste à n'utiliser sur les premières lettres tant que celle-ci constitue le début d'une unique commande.
Résultat : |
jshell> /l
1 : 1+2
2 : "bonjour"
jshell> /list
1 : 1+2
2 : "bonjour"
Dans l'exemple ci-dessus, la commande /list peut être abrégée /l car c'est la seule commande qui commence par la lettre « L ».
Cette abréviation peut aussi s'utiliser sur les options des commandes
Résultat : |
jshell> /l -a
La commande ci-dessus est une version abrégée de /list -all
Si l'abréviation saisie n'est pas unique alors un message d'erreur est affiché
Résultat : |
jshell> /s
| Command: '/s' is ambiguous: /save, /set
| Type /help for help.
Dans l'exemple ci-dessus, l'abréviation /s n'est pas unique car il existe deux commandes /set et /save comme indiqué dans le message d'erreur.
En cas de doute, il est possible d'utiliser la touche tabulation : si plusieurs commandes commencent par les caractères saisis alors elles sont affichées.
JShell utilise la bibliothèque JLine2 pour gérer la saisie des fragments et des commandes.
Il est possible d'ajouter du texte à la position du curseur simplement en le saisissant.
JShell propose de nombreuses fonctionnalités, généralement sous la forme d'appui sur une touche ou une combinaison de touches pour permettre la saisie des fragments et des commandes
Ces combinaisons peuvent utiliser, entre autres, les touches :
Plusieurs touches ou combinaisons de touches peuvent être utilisées pour naviguer dans la ligne courante en cours de saisie.
Touche ou combinaison de touches |
Rôle |
Flèche gauche |
Se déplacer d'un caractère à gauche |
Flèche droite |
Se déplacer d'un caractère à droite |
Retour chariot (Return) |
Valider la ligne courante |
Home |
Se déplacer au début de la ligne |
Fin (End) |
Se déplacer au début de la ligne |
Meta+B |
Se déplacer au début du mot précédent |
Meta+F |
Se déplacer à la fin du mot suivant |
Remarque : le parcours de l'historique de navigation contenant un fragment multilignes se fait ligne par ligne.
JShell conserve un historique des fragments et des commandes saisies dans la session.
Plusieurs touches ou combinaisons de touches peuvent être utilisées pour naviguer dans l'historique.
Touche ou combinaison de touches |
Rôle |
Flèche haut |
Remplacer la ligne courante par le contenu de la ligne précédente dans l'historique |
Flèche bas |
Remplacer la ligne courante par le contenu de la ligne suivante dans l'historique |
Ctrl + flèche haut |
Remplacer la ligne courante par le contenu de la première ligne du fragment ou commande précédente dans l'historique |
Remarque : si un fragment est composé de plusieurs lignes alors l'utilisation des flèches haut et bas permet de naviguer dans chacune des lignes du fragments
Cette navigation permet de ressaisir un fragment ou une commande, éventuellement avec une modification, simplement en appuyant sur la touche Entrée (Enter). Cela évite d'avoir à ressaisir l'intégralité de la ligne.
Si la session est réinitialisée (par exemple en utilisant la commande /reset), l'historique est conservé et il est donc toujours possible de naviguer dedans.
Le texte saisi est inséré à la position du curseur dans la ligne courante.
Plusieurs combinaisons de touches peuvent être utilisées lors de la saisie d'une ligne :
Combinaison de touches |
Rôle |
Suppr (Del ou Delete) |
Supprimer le caractère après le curseur |
Backspace |
Supprimer le caractère avant le curseur |
Ctrl+K |
Supprimer le reste de la ligne à partir du curseur |
Ctrl+U |
Supprimer le début de la ligne jusqu'au curseur |
Alt+D |
Supprimer le reste du mot à partir du curseur |
Ctrl+W |
Supprimer le texte du curseur jusqu'à l'espace précédent |
Ctrl+Y |
Coller la dernière portion de code supprimée |
Alt+Y |
Après un Ctrl+Y, permettre de sélectionner le texte précédemment supprimé et ce de manière cyclique |
Entrée (Enter) |
Valider la saisie ou la modification pour évaluation |
Que la ligne soit modifiée ou pas, l'appui sur la touche « Entrée » permet de demander l'évaluation du fragment.
JShell permet de faire une recherche dans l'historique des fragments et commandes saisis. Cela permet de plus facilement retrouver une ligne saisie sans avoir à naviguer ligne par ligne dans l'historique.
Pour demander une recherche, il faut utiliser la combinaison de touches Ctrl+R, puis de saisir tout ou partie de l'élément recherché. L'invite de commande est remplacée par celle de recherche :
Résultat : |
(reverse-i-search)`':
La recherche commence à partir de la dernière ligne saisie et remonte dans l'historique.
Résultat : |
jshell> /history
void saluer() {
System.out.println("Bonjour");
}
saluer()
void saluer() {
System.out.println("Hello");
}
saluer()
/history
(reverse-i-search)`sal': saluer()
La ligne courante trouvée par la recherche est affichée après le caractère deux-points. La recherche est incrémentale : les propositions s'affichent au fur et à mesure de la saisie du motif à rechercher.
Il est possible de naviguer dans les propositions en utilisant des combinaisons de touches :
Il est possible de définir une macro en utilisant deux combinaisons de touches :
Raccourci |
Rôle |
Ctrl+x ( |
Débuter l'enregistrement d'une macro |
Ctrl+x ) |
Stopper l'enregistrement d'une macro |
Pour définie une macro, il faut utiliser la combinaison de touches Ctrl+X (, saisir le texte de la macro et utiliser la combinaison de touches Ctrl+X ).
Pour invoquer la macro et obtenir son contenu, il faut utiliser la combinaison de touches Ctrl+x e.
La commande /edit permet de modifier un ou plusieurs fragments en utilisant par défaut l'éditeur de texte intégré dans JShell.
Il est possible d'utiliser un éditeur de texte externe pour modifier le code d'un fragment. Il est possible de configurer JShell pour utiliser l'éditeur de texte de son choix.
JShell recherche si une des variables d'environnement ci-dessous est définie. Si c'est le cas, la valeur est utilisée pour désigner l'éditeur externe de texte à utiliser pour modifier les fragments.
Si aucune n'est définie et qu'aucun éditeur n'est configuré alors c'est l'éditeur de texte par défaut fourni avec JShell qui est utilisé. Ce dernier est très simple car il ne permet que de saisir du texte multi-lignes.
La commande /set avec l'option editor permet de configurer l'éditeur externe à utiliser.
Exemple ( code Java 9 ) : |
jshell> /set editor notepad
| Editor set to: notepad
jshell> /edit
La fermeture de l'éditeur externe est obligatoire pour permettre le retour du prompt de JSHell.
JShell propose des fonctionnalités pour faciliter la saisie du code.
Il suffit de saisir le début du code et d'appuyer sur la touche tab pour obtenir des suggestions contextuelles.
Si une seule suggestion est trouvée, elle est utilisée.
Si plusieurs suggestions sont trouvées alors celles-ci sont affichées.
Exemple ( code Java 9 ) : |
jshell> Sys<tab>
jshell> System
System
jshell> System.o<tab>
jshell> System.out
out
jshell> System.out.print<tab>
print( printf( println(
jshell> System.out.print
Il est possible d'utiliser la combinaison de touche « Shift » puis « Tab » pour afficher la liste de surcharges d'une méthode.
Exemple ( code Java 9 ) : |
jshell> String chaine = "Bonjour"
chaine ==> "Bonjour"
jshell> chaine.substring(<shift><tab>
Signatures:
String String.substring(int beginIndex)
String String.substring(int beginIndex, int endIndex)
<press tab again to see documentation>
En appuyant sur la touche « Tab » immédiatement après l'affichage de la liste, la Javadoc de chaque surcharge est affichée successivement en appuyant à chaque fois sur la touche « tab ».
Exemple ( code Java 9 ) : |
jshell> chaine.substring(<tab>
String String.substring(int beginIndex)
Returns a string that is a substring of this string.The substring begins with the character at
the specified index and extends to the end of this string.
Examples:
"unhappy".substring(2) returns "happy"
"Harbison".substring(3) returns "bison"
"emptiness".substring(9) returns "" (an empty string)
Parameters:
beginIndex - the beginning index, inclusive.
Returns:
the specified substring.
<press tab to see next documentation>
jshell> chaine.substring(<tab>
String String.substring(int beginIndex, int endIndex)
Returns a string that is a substring of this string.The substring begins at the specified
beginIndex and extends to the character at index endIndex - 1 . Thus the length of the
substring is endIndex-beginIndex .
Examples:
"hamburger".substring(4, 8) returns "urge"
"smiles".substring(1, 5) returns "mile"
Parameters:
beginIndex - the beginning index, inclusive.
endIndex - the ending index, exclusive.
Returns:
the specified substring.
<press tab again to see all possible completions; total possible completions: 545>
jshell> chaine.substring(
Après avoir saisi la première parenthèse d'une méthode, l'utilisation de la touche Shift puis sur la touche Tab (attention, ce n'est pas une combinaison qui a un autre rôle) permet d'afficher les valeurs utilisables et le cas échant les différentes surcharges de la méthode.
Exemple ( code Java 9 ) : |
jshell> int debut = 10
debut ==> 10
jshell> chaine.subs<tab>
substring(
jshell> chaine.substring(<shift><tab>
debut
Signatures:
String String.substring(int beginIndex)
String String.substring(int beginIndex, int endIndex)
<press tab again to see documentation>
jshell> chaine.substring(
Comme avec JShell le code est évalué séquentiellement au fur et à mesure de sa saisie, il est possible de faire référence à des membres ou des types qui ne sont pas encore défini. Ces références sont alors non résolues jusqu'à leur définition et sont désignées par le terme référence à venir.
JShell supporte les références à venir (forward reference) dans les classes, les méthodes et les variables.
Exemple ( code Java 9 ) : |
jshell> double calculerMontantTTC(double montantHT) {
...> return montantHT * (1 + ((double)TVA /100));
...> }
| created method calculerMontantTTC(double), however, it cannot be invoked until variable
TVA is declared
La méthode est définie mais elle ne peut être invoquée tant que la variable TVA n'est pas définie.
Exemple ( code Java 9 ) : |
jshell> int TVA = 20
TVA ==> 20
jshell> calculerMontantTTC(100)
$8 ==> 120.0
C'est notamment le cas si le type de la redéfinition n'est pas compatible avec le précédent type. Il est important de noter que JShell nous informe par avance des incompatibilités uniquement dans le mode de feedback verbose. Dans les autres modes, l'erreur n'est affichée qu'à l'exécution.
Exemple ( code Java 9 ) : |
jshell> int TVA = 20
TVA ==> 20
jshell> /set feedback verbose
| Feedback mode: verbose
jshell> BigInteger TVA = new BigInteger("20")
TVA ==> 20
| replaced variable TVA : BigInteger
| update modified method calculerMontantTTC(double) which cannot be invoked until
this error is corrected:
| incompatible types: java.math.BigInteger cannot be converted to double
| return montantHT * (1 + ((double)TVA /100));
| ^-^
| update overwrote variable TVA : int
Les références à venir peuvent aussi concerner des méthodes ou des types.
Exemple ( code Java 9 ) : |
jshell> class MaClasse {
...> AutreClasse autre;
...> }
| created class MaClasse, however, it cannot be referenced until class AutreClasse is declared
jshell> new MaClasse()
| Error:
| cannot find symbol
| symbol: class MaClasse
| new MaClasse()
| ^------^
jshell> class AutreClasse { }
| created class AutreClasse
jshell> new MaClasse()
$3 ==> MaClasse@490ab905
JShell est un outil qui facilite l'expérimentation de code Java : il permet donc facilement de modifier ou de redéfinir une variable, une méthode ou une classe. Il suffit de ressaisir la définition du fragment de code. Si l'élément est déjà défini, sa définition est simplement remplacée.
Il est possible de redéclarer une variable, une méthode ou une classe.
Exemple ( code Java 9 ) : |
jshell> /list
1 : int valeur = 10;
jshell> int valeur = 20;
valeur ==> 20
jshell> /list
2 : int valeur = 20;
Il est possible que la redéfinition de l'élément soit incompatible avec la définition existante.
Par exemple, si la redéfinition d'une variable change son type.
Exemple ( code Java 9 ) : |
jshell> int valeur = 10;
valeur ==> 10
jshell> /list
1 : int valeur = 10;
jshell> String valeur = "10";
valeur ==> "10"
jshell> /list
2 : String valeur = "10";
Attention, la redéfinition d'un élément peut induire des erreurs à l'exécution du code qui en dépend. Donc ce type d'opération n'est pas forcement sans risque ni conséquence.
Exemple ( code Java 9 ) : |
jshell> BigInteger TVA = new BigInteger("20")
TVA ==> 20
jshell> calculerMontantTTC(100)
| attempted to call method calculerMontantTTC(double) which cannot be invoked until
this error is corrected:
| incompatible types: java.math.BigInteger cannot be converted to double
| return montantHT * (1 + ((double)TVA /100));
| ^-^
Il est possible de redéfinir une méthode.
Exemple ( code Java 9 ) : |
jshell> void saluer(String nom) {
...> System.out.println("Bonjour "+nom);
...> }
| created method saluer(String)
jshell> /list
2 : String message = "Bonjour";
7 : void saluer(String nom) {
System.out.println("Bonjour "+nom);
}
jshell> void saluer(String nom) {
...> System.out.println("Salut "+nom);
...> }
| modified method saluer(String)
jshell> /list
2 : String message = "Bonjour";
8 : void saluer(String nom) {
System.out.println("Salut "+nom);
}
Lors de la redéfinition d'un élément, JShell affiche comme résultat « modified ... ». Dans le cas d'une méthode, la signature de la méthode ne doit pas être changée pour que cela soit une redéfinition.
Exemple ( code Java 9 ) : |
jshell> void saluer(String nom) {
...> System.out.println("Bonjour "+nom);
...> }
| created method saluer(String)
jshell> /list
1 : void saluer(String nom) {
System.out.println("Bonjour "+nom);
}
jshell> void saluer(int nom) {
...> System.out.println("Bonjour "+nom);
...> }
| created method saluer(int)
jshell> /list
1 : void saluer(String nom) {
System.out.println("Bonjour "+nom);
}
2 : void saluer(int nom) {
System.out.println("Bonjour "+nom);
}
Les exceptions de type checked levées par un fragment de code qui n'est pas dans un bloc de code sont gérées par JShell : il n'est donc pas utile de les capturer.
Il n'est donc pas nécessaire de gérer les exceptions pour les fragments évalués :
Exemple ( code Java 9 ) : |
jshell> Thread.sleep(5000);
jshell>
L'évaluation du fragment de l'exemple ci-dessus fait attendre 5 secondes avant de rendre la main.
Cela s'applique aux fragments au fur et à mesure de leur saisie et évaluation.
Exemple ( code Java 9 ) : |
jshell> Path path = Paths.get("fichier.txt")
path ==> fichier.txt
jshell> Stream<String> lignes = Files.lines(path)
lignes ==> java.util.stream.ReferencePipeline$Head@3327bd23
jshell> lignes.forEach(l -> System.out.println(l))
ligne1
ligne2
ligne3
Dans l'exemple ci-dessous, il n'est pas utile de gérer les exceptions de type IOException.
Dans un bloc de code, les exceptions de type checked doivent être gérées : la gestion des exceptions de type checked est donc toujours requise dans le code d'une méthode.
Exemple ( code Java 9 ) : |
jshell> class MonThread extends Thread {
...> public void run() {
...> Thread.sleep(5000);
...> }
...> }
| Error:
| unreported exception java.lang.InterruptedException; must be caught or declared to be thrown
| Thread.sleep(5000);
| ^----------------^
La gestion des exceptions de type checked est toujours requise dans le code d'une expression Lambda.
Exemple ( code Java 9 ) : |
jshell> Thread monThread = new Thread(() -> { Thread.sleep(5000); });
| Error:
| unreported exception java.lang.InterruptedException; must be caught or declared to be thrown
| Thread monThread = new Thread(() -> { Thread.sleep(1000); });
| ^----------------^
Lorsqu'une exception est levée lors de l'exécution du code d'une méthode, la stacktrace est affichée. Dans la stacktrace, la ligne du fragment concerné est identifiée sous la forme #id_fragment:numero_de_ligne.
Exemple ( code Java 9 ) : |
jshell> double calculerMoyenne(int... valeurs) {
...> long somme = 0;
...> for(int val : valeurs) {
...> somme += val;
...> }
...> return somme / valeurs.length;
...> }
| created method calculerMoyenne(int...)
jshell> calculerMoyenne(new int []{5,10,15})
$22 ==> 10.0
| created scratch variable $22 : double
jshell> calculerMoyenne(new int []{})
| java.lang.ArithmeticException thrown: / by zero
| at calculerMoyenne (#21:6)
| at (#23:1)
JShell facilite l'ajout d'imports lors de la saisie de code : il suffit de saisir le nom d'une classe et d'utiliser la combinaison de touches « Shift + Tab » et d'appuyer sur la touche « i »
Exemple ( code Java 9 ) : |
jshell> LocalDateTime<Shift+tab, i>
0: Do nothing
1: import: java.time.LocalDateTime
Choice: 1
Imported: java.time.LocalDateTime
jshell> LocalDateTime.now()
$2 ==> 2018-02-20T22:51:05.310054600
JShell fait alors plusieurs propositions selon les classes pleinement qualifiées trouvées. Il suffit alors de saisir le chiffre correspond à l'action choisie.
Si aucune classe n'est trouvée, alors un message est affiché pour indiquer l'échec à trouver une classe à importer.
Exemple ( code Java 9 ) : |
jshell> LocalDataTime<Shift+tab, i>
No candidate fully qualified names found to import.
Il est possible de créer une variable à partir d'une expression. Il suffit de saisir l'expression et d'utiliser la combinaison de touches « Shift + Tab » puis d'appuyer sur la touche « v »
Exemple ( code Java 9 ) : |
jshell> 1 + 2 <shift+tab, v>
jshell> int | = 1 + 2
Le curseur, représenté par le caractère « | » (barre verticale) est placé sur la ligne à l'endroit où il faut saisir le nom de la variable. Le type est déterminé en évaluant l'expression. La valeur affectée à la variable est l'expression elle-même.
Remarque : pour que l'opération puisse être effectuée, l'expression doit être valide. Si ce n'est pas le cas, la combinaison de touches « Shift + Tab » suivi de la touche « v » n'a aucun effet.
Lors de la demande de la création d'une variable à partir d'une expression, il est possible que le type ne soit pas encore importé. Dans ce cas, la combinaison de touche « Shift + Tab » puis « v » permet de créer la variable et l'import du type.
Exemple ( code Java 9 ) : |
jshell> JFrame frame = new JFrame()
frame ==> javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden, ... tPaneCheckingEnabled=true]
jshell> frame.getSize()<shift+tab, v>
0: Do nothing
1: Create variable
2: import: java.awt.Dimension. Create variable
Choice: 2
Imported: java.awt.Dimension
jshell> Dimension |= frame.getSize()
Dans l'exemple ci-dessus, il suffit de sélectionner l'option 2.
Il est aussi possible d'utiliser les imports static.
Exemple ( code Java 9 ) : |
jshell> import static java.lang.System.out
jshell> out.println("Bonjour")
Bonjour
Il est possible d'utiliser des scripts pour configurer une session JShell (imports, définition de fragments, ...) qui seront dès lors utilisable dans la session tant que celle-ci n'est pas terminée ou réinitialisée.
Un script JShell est un fichier texte qui contient une séquence de fragments et/ou commandes chacun sur une ligne dédiée.
Il existe trois scripts prédéfinis :
Nom |
Rôle |
DEFAULT |
Il définit la déclaration de certains imports dans la session. C'est le script utilisé par défaut si aucun autre script n'est précisé |
PRINTING |
Il définit des méthodes print(), println() et printf() pour envoyer des données sur la sortie standard |
JAVASE |
Il importe les packages de Java SE Core (ceux définis dans le module java.se). Cela rend le démarrage de JShell très long |
Un script peut être créé grâce à un éditeur de texte ou en utilisant la commande /save.
Les scripts de démarrage sont rechargés chaque fois que la session JShell est réinitialisée : soit au démarrage de JShell et à l'utilisation des commandes /reset, /load et /env.
Il est possible d'utiliser un des scripts prédéfinis ou d'écrire ses propres scripts pour réponde à ses besoins.
Si aucun script n'est explicitement configuré, alors c'est le script DEFAULT qui est chargé.
Il est possible de configurer un ou plusieurs scripts de démarrage de différente manière.
Il est possible d'utiliser la commande /set start suivi du nom du fichier qui contient le script ou d'un des scripts prédéfinis. Pour que cela soit pris en compte, il faut réinitialiser la session.
Le script PRINTING permet d'utiliser des surcharges des méthodes print(), println() et printf(), évitant ainsi l'ajout de System.out pour afficher du texte.
Exemple ( code Java 9 ) : |
C:\java>jshell
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
jshell> /set start PRINTING
jshell> /methods
jshell> /reset
| Resetting state.
jshell> /methods
| void print(boolean)
| void print(char)
| void print(int)
| void print(long)
| void print(float)
| void print(double)
| void print(char s[])
| void print(String)
| void print(Object)
| void println()
| void println(boolean)
| void println(char)
| void println(int)
| void println(long)
| void println(float)
| void println(double)
| void println(char s[])
| void println(String)
| void println(Object)
| void printf(java.util.Locale,String,Object...)
| void printf(String,Object...)
Par défaut, la configuration effectuée par la commande /set n'est effective que pour la session courante. Pour rendre la configuration courante permanente, il faut utiliser l'option -retain de la commande /set.
L'option -retain permet de rendre la configuration courante celle par défaut pour les futures sessions de JShell.
Exemple ( code Java 9 ) : |
jshell> /set start -retain
jshell>
Les scripts de démarrage sont exécutés à chaque réinitialisation de la session. Le contenu du script est chargé une seule fois au moment de l'exécution de la commande /set start puis stocké. Les scripts prédéfinis peuvent être modifiés dans les versions futures de Java.
Il est possible de configurer plusieurs scripts de démarrage avec la commande /set start.
Exemple ( code Java 9 ) : |
jshell> /set start -retain DEFAULT PRINTING
jshell> /exit
| Goodbye
C:\java> jshell
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
jshell> println("bonjour")
bonjour
jshell>
La commande /set start sans argument permet de visualiser les scripts de démarrage.
Exemple ( code Java 9 ) : |
jshell> /set start
| /set start -retain DEFAULT PRINTING
| ---- DEFAULT ----
| 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.*;
| ---- PRINTING ----
| void print(boolean b) { System.out.print(b); }
| void print(char c) { System.out.print(c); }
| void print(int i) { System.out.print(i); }
| void print(long l) { System.out.print(l); }
| void print(float f) { System.out.print(f); }
| void print(double d) { System.out.print(d); }
| void print(char s[]) { System.out.print(s); }
| void print(String s) { System.out.print(s); }
| void print(Object obj) { System.out.print(obj); }
| void println() { System.out.println(); }
| void println(boolean b) { System.out.println(b); }
| void println(char c) { System.out.println(c); }
| void println(int i) { System.out.println(i); }
| void println(long l) { System.out.println(l); }
| void println(float f) { System.out.println(f); }
| void println(double d) { System.out.println(d); }
| void println(char s[]) { System.out.println(s); }
| void println(String s) { System.out.println(s); }
| void println(Object obj) { System.out.println(obj); }
| void printf(java.util.Locale l, String format, Object... args) { System.out.printf(l,
format, args); }
| void printf(String format, Object... args) { System.out.printf(format, args); }
Il est possible d'utiliser l'option --startup de la commande jshell pour préciser un script de démarrage.
Exemple ( code Java 9 ) : |
C:\java>jshell --startup PRINTING
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
jshell> /methods
| void print(boolean)
| void print(char)
| void print(int)
| void print(long)
| void print(float)
| void print(double)
| void print(char s[])
| void print(String)
| void print(Object)
| void println()
| void println(boolean)
| void println(char)
| void println(int)
| void println(long)
| void println(float)
| void println(double)
| void println(char s[])
| void println(String)
| void println(Object)
| void printf(java.util.Locale,String,Object...)
| void printf(String,Object...)
jshell> println("bonjour")
bonjour
Il est possible de préciser plusieurs scripts en utilisant pour chacun une option --startup.
Exemple ( code Java 9 ) : |
C:\java>jshell --startup DEFAULT --startup PRINTING
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
Un script peut être créé avec un simple éditeur de texte ou créer avec JShell.
La commande /save permet de créer des scripts à partir de la session courante.
La commande la plus simple est /save suivi d'un nom de fichier. Les fragments de la session courante sont alors enregistrés dans le fichier précisé.
Exemple ( code Java 9 ) : |
jshell> /save test.jsh
jshell>
Il est possible de demander l'enregistrement dans un fichier de l'historique des fragments et des commandes saisies qu'elles soient valides ou invalides en utilisant l'option -history.
Exemple ( code Java 9 ) : |
jshell> /save -history test.jsh
jshell>
L'option -start de la commande /save permet d'enregistrer dans un fichier le script de démarrage courant.
Exemple ( code Java 9 ) : |
jshell> /save -start test.jsh
jshell>
Il est possible de demander à JShell de charger et d'exécuter un script à son lancement simplement en précisant le nom du fichier en paramètre de la commande jshell.
Exemple ( code Java 9 ) : |
C:\java> jshell test.jsh
bonjour
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
Il est aussi possible d'utiliser la commande /open pour charger et exécuter un script
Exemple ( code Java 9 ) : |
C:\java> jshell
| Welcome to JShell -- Version 9.0.1
| For an introduction type: /help intro
jshell> print("coucou")
coucou
jshell> /open test.jsh
bonjour
jshell> /list
1 : print("coucou")
2 : println("bonjour")
La commande /open peut être utilisée pour charger l'intégralité d'un fichier .java et permettre d'ajouter dans la session la ou les classes définies dans le fichier fourni en paramètre.
Exemple ( code Java 9 ) : |
jshell> print("bonjour")
bonjour
jshell> /open Hello.java
jshell> /list
1 : print("bonjour")
2 : public class Hello {
public static void main(String... args) {
System.out.println("Hello");
}
}
jshell>
Un mode de feedback est une configuration nommée qui précise quelles sont les informations restituées par JShell suite à l'évaluation d'un fragment.
JShell propose par défaut quatre modes de feedback qui définissent le niveau de verbosité des informations fournies par JShell suite à l'évaluation d'un fragment :
Mode |
Fragments avec valeur |
Déclaration de types et annotations |
Mise à jour |
Commandes exécutées avec succès |
Prompt utilisé |
verbose |
name ==> value avec déclaration |
Oui |
Oui |
Oui |
\njshell> |
normal |
name ==> value |
Oui |
Non |
Oui |
\njshell> |
Concise |
name ==> value (uniquement pour les expressions) |
Non |
Non |
Non |
\njshell> |
Silent |
Non |
Non |
Non |
-> |
La commande /set avec l'option feedback permet de sélectionner le mode de feedback à utiliser en le précisant à la suite.
Résultat : |
jshell> /set feedback verbose
| Feedback mode: verbose
jshell> int i = 10
i ==> 10
| modified variable i : int
| update overwrote variable i : int
jshell> /set feedback normal
| Feedback mode: normal
jshell> int i = 10
i ==> 10
jshell> /set feedback concise
jshell> int i = 10
jshell> /set feedback silent
-> int i = 10
-> /set feedback normal
| Feedback mode: normal
jshell>
Pour connaitre le mode de feedback actif, il suffit d'utiliser la commande /set avec l'option feedback. Elle est indiquée avec la commande qui permet de l'activer suivi de la liste des modes utilisables.
Résultat : |
jshell> /set feedback
| /set feedback normal
|
| Available feedback modes:
| concise
| normal
| silent
| verbose
Le mode de feedback par défaut est normal.
Pour obtenir le mode de feedback courant, il suffit d'invoquer la commande /set feedback sans options
Exemple ( code Java 9 ) : |
jshell> /set feedback
| /set feedback verbose
|
| Available feedback modes:
| concise
| normal
| silent
| verbose
Le mode courant est indiqué sur la première ligne sous la forme de la commande utilisée pour configurer ce mode.
Il est possible de définir son propre mode de feedback :
Les modes de feedback prédéfinis ne peuvent pas être modifiés. Cependant, il est possible de créer un mode de feedback personnalisé.
Un mode de feedback personnalisés peut être créé sur la base de la copie d'un mode existant.
Résultat : |
jshell> /set mode monmode normal -command
| Created new feedback mode: monmode
Cet exemple créé un mode personnalisé nommé monmode qui est une copie du mode normal. L'option -command demande un retour sur les actions des commandes exécutées. Si ce n'est pas souhaité, il est possible d'utiliser l'option -quiet.
Plusieurs éléments peuvent être configurés dans un mode de feedback
La commande /set avec l'option prompt permet de définir les prompts.
La commande /set prompt sans autres options affiche les prompts des différents modes.
Exemple ( code Java 9 ) : |
jshell> /set prompt
| /set prompt normal "\njshell> " " ...> "
| /set prompt silent "-> " ">> "
| /set prompt concise "jshell> " " ...> "
| /set prompt verbose "\njshell> " " ...> "
Il est possible de demander l'utilisation des prompts d'un mode particulier en utilisant la commande /set prompt suivi du mode souhaité.
Exemple ( code Java 9 ) : |
jshell> /set prompt normal
| /set prompt normal "\njshell> " " ...> "
Il est possible de définir les prompts pour un mode particulier simplement en précisant le nom du mode concerné et les valeurs des prompts normaux et de suite.
Exemple ( code Java 9 ) : |
jshell> /set prompt monmode "\nMon prompt> " ".....> "
Le mode précisé doit exister sinon une erreur est affichée.
Exemple ( code Java 9 ) : |
jshell> /set prompt monautremode
"\nMon prompt> "
".....> "
| Does not match any current feedback mode: monautremode
-- /set prompt monautremode "\nMon prompt> " ".....> "
| Available feedback modes:
| concise
| normal
| silent
| verbose
| See /help /set prompt for help.
Pour activer un mode, il faut utiliser la commande /set prompt suivi du mode
Exemple ( code Java 9 ) : |
jshell> /set prompt monmode
| /set prompt monmode "\nMon prompt> " ".....> "
jshell>
jshell> /set feedback
monmode
| Feedback mode: monmode
Mon prompt> if (true) {
.....> i =0;
.....> }
| Error:
| cannot find symbol
| symbol:
variable i
| i =0;
| ^
Mon prompt>
La configuration n'est pas impactée par la commande /reset.
La configuration demandée n'est utilisée que dans la session courante. Pour quelle soit activée pour toutes les sessions, il faut utiliser l'option -retain.
Exemple ( code Java 9 ) : |
jshell> /set prompt monmode
| /set prompt monmode "\nMon prompt> " ".....> "
jshell> /set feedback
monmode
| Feedback mode: monmode
Mon prompt> if (true) {
.....> int i =0;
.....> }
Mon prompt>
Il est possible d'utiliser le motif «%s» dans le prompt : il sera alors remplacé par le prochain identifiant d'un fragment.
Exemple ( code Java 9 ) : |
jshell> /set prompt monmode
"\nMon prompt %s> " ".....> "
jshell> /set feedback
monmode
| Feedback mode: monmode
Mon prompt 2> /list
1 : if (true) {
int i = 0;
}
Mon prompt 2>
Cet identifiant ne sera attribué que si le fragment est évalué sans erreur.
Exemple ( code Java 9 ) : |
Mon prompt 2> test
| Error:
| cannot find symbol
| symbol:
variable test
| test
| ^--^
Mon prompt 2> int i = 0;
i ==> 0
Mon prompt 3>
Lorsque les valeurs affichées dépassent une certaine taille, celles-ci sont tronquées.
Pour connaître la taille maximale d'un mode, il faut utiliser la commande /set suivi de l'option truncation.
Exemple ( code Java 9 ) : |
jshell> /set truncation
normal
| /set truncation normal 80
| /set truncation normal 1000 expression,varvalue
Pour modifier sa taille maximale, il faut utiliser la commande /set suivi de l'option truncation et de la taille souhaitée. Cette taille peut être suivie d'un sélecteur pouvant appartenir à deux types :
Type de sélecteur |
|
Case |
Permet de sélectionner un type de fragments. Les valeurs possibles sont : vardecl : déclaration d'une variable sans initialisation varinit : declaration d'une variable avec initialisation expression : expression varvalue : valeur d'une expression assignment : assignation une variable |
Action |
Permet de sélectionner les actions réalisées durant l'évaluation du fragment. Les valeurs possibles sont : added : le fragment est ajouté modified : le fragment est modifié replaced : le fragment est remplacé par un autre |
Plusieurs sélecteurs peuvent être utilisés : il faut séparer chacun d'entre eux avec une virgule.
L'aide en ligne fournit une description complète des sélecteurs : celle-ci est obtenue en invoquant la commande :
/help /set truncation
Exemple ( code Java 9 ) : |
jshell> /set truncation
monmode
| /set truncation monmode 80
| /set truncation monmode 1000 expression,varvalue
jshell> /set truncation
monmode 80 expression,varvalue
jshell> /set feedback
monmode
| Feedback mode: monmode
Mon prompt> texte
texte ==>
"012345678901234567890123456789012345678901234567 ...
5678901234567890123456789"
Attention si plusieurs troncatures sont définies : l'ordre de définition des différentes troncatures est important. Il faut les définir des plus globales ou plus précises.
Le format de retour de l'évaluation d'un fragment est défini pour chaque mode sauf pour le mode silent. Par exemple dans le mode normal, le type d'une variable n'est pas affiché ou l'utilisation d'un import n'affiche rien.
Exemple ( code Java 9 ) : |
jshell> import java.time.*;
jshell> 10L
$2 ==> 10
Le format de retour de l'évaluation d'un fragment peut être personnalisé.
Pour connaitre le format d'un mode, il faut utiliser la commande /set avec l'option format suivi du nom de mode.
Exemple ( code Java 9 ) : |
jshell> /set format normal
| /set format normal action "created" added-primary
| /set format normal action "modified" modified-primary
| /set format normal action "replaced" replaced-primary
| /set format normal action "overwrote" overwrote-primary
| /set format normal action "dropped" dropped-primary
...
| /set format normal display "{result}{pre}created scratch variable
{name} : {type}{post}" expression-added,modified,replaced-primary
| /set format normal display "{result}{pre}value of {name} :
{type}{post}" varvalue-added,modified,replaced-primary
| /set format normal display "{result}{pre}assigned to {name} : {type}{post}"
assignment-primary
| /set format normal display "{result}{pre}{action} variable {name} :
{type}{resolve}{post}" vardecl,varinit
...
Il y a de nombreuses options de configuration pour les différents types de messages qui peuvent être affichés.
La commande /set format possède donc de nombreuses options pour configurer le message en retour de l'évaluation d'un fragment.
La syntaxe de la commande est de la forme :
/set format <mode> <field> "<format>" <selector>...
Il est possible d'obtenir le détail de ces options en utilisant la commande /help /set format car les valeurs utilisables sont riches.
Il est possible d'ajouter des classes via le classpath ou le modulepath pour être utilisées dans la session de JShell.
L'option --class-path permet de définir le classpath qui sera utilisable durant la session de JShell. Le classpath peut classiquement contenir des répertoires ou des fichiers jar.
Résultat : |
C:\java> jshell --class-path netty-all-4.1.6.Final.jar
| Welcome to JShell -- Version 11
| For an introduction type: /help intro
jshell>
Il est possible d'utiliser le caractère * comme joker pour indiquer tous les fichiers du chemin. Ils seront alors ajoutés au classpath.
Pour utiliser les classes ajoutées dans le classpath, il suffit de les importer. Il n'est donc pas possible d'utiliser des classes qui soient dans le package par défaut.
Résultat : |
jshell> import io.netty.util.*
Il est aussi possible d'utiliser la commande /env pour définir le classpath durant l'exécution de JShell.
Résultat : |
jshell> /env
jshell> /env --class-path netty-all-4.1.6.Final.jar
| Setting new options and restoring state.
jshell> /env
| --class-path netty-all-4.1.6.Final.jar
jshell>
La commande /env réinitialise l'état de la session, recharge les fragments avec le nouveau classpath.
Il est possible de définir le modulepath en utilisant l'option --module-path de JShell.
Résultat : |
C:\java> jshell --module-path ./modules
Il est aussi possible d'ajouter des modules au graphe de modules en utilisant l'option --add-modules de JShell
Résultat : |
C:\java> jshell --module-path ./modules --add-modules utils.jar
Il est possible d'utiliser la commande /env pour connaitre la définition de l'environnement courant.
Résultat : |
jshell> /env
| --module-path .\modules
Il est possible d'obtenir la liste complète des options en utilisant l'option --help ou -h de jshell en ligne de commande
Option |
Rôle |
--class-path <path> |
Préciser les éléments à ajouter dans le classpath |
--module-path <path> |
Préciser les éléments à ajouter dans le modulepath |
--add-modules <module>(,<module>)* |
Ajouter un module au graphe de modules |
--enable-preview |
Activer les fonctionnalités en preview |
--startup <file> |
Remplacer les scripts de démarrage par celui précisé uniquement pour cette session |
--no-startup |
Ne pas exécuter de script de démarrage |
--feedback <mode> |
Préciser le mode de feedback à utiliser (silent, concise, normal, verbose ou un mode personnalisé précédemment défini) |
-q |
Activer le mode de feedback concis. Equivalent à --feedback concise |
-s |
Activer le mode de feedback silencieux. Equivalent à --feedback silent |
-v |
Activer le mode de feedback verbeux. Equivalent à --feedback verbose |
-J<flag> |
Passer des options à l'environnement d'exécution. Il faut utiliser une option -J pour chaque option |
-C<flag> |
Passer des options au compilateur. Il faut utiliser une option -C pour chaque option |
--version |
Afficher la version et rend la main |
--show-version |
Afficher la version avant de lancer JShell |
--help, -?, -h |
Afficher l'aide sur les options standard et rend la main |
--help-extra, -X |
Afficher l'aide sur les options non standard et rend la main |
Il est possible de fournir en paramètre le nom d'un fichier ou un des noms de fichier prédéfinis ( DEFAULT, PRINTING ou JAVASE). Le fichier indiqué est exécuté comme script de démarrage de JShell.
Résultat : |
C:\java> type test.jsh
println("bonjour")
C:\java> jshell test.jsh
bonjour
| Welcome to JShell -- Version 11
| For an introduction type: /help intro
jshell>
Il est aussi possible d'utiliser le caractère « - » pour indiquer d'utiliser l'entrée standard : attention dans ce cas JShell est moins interactif.
Résultat : |
C:\java> jshell -
System.out.println("hello");
hello
int i = 1;
System.out.println(i);
^C
JShell n'est pas qu'un outil : c'est aussi une API qui permet d'utiliser les fonctionnalités de JShell dans une application et pas uniquement comme outil en ligne de commande. Cette API permet d'évaluer dynamiquement des fragments de code et d'obtenir le résultat correspondant.
L'API est encapsulée dans le module jdk.jshell qui contient 4 packages :
Il est possible de créer une instance de type jdk.jshell.JShell et de l'utiliser de manière programmatique.
Pour obtenir une telle instance il faut utiliser la fabrique create() de la classe JShell. Comme elle implémente l'interface AutoClosable, il est possible de définir cette instance dans un try with resources qui gérera l'invocation de la méthode close().
Les fragments sont encapsulés dans des instances de type jdk.jshell.Snippet.
La classe jdk.jshell.SnippetEvent encapsule un événement lié à l'évaluation d'un fragment.
La méthode eval() évalue le fragment fourni en paramètre et renvoie une List de SnippetEvent qui sont des événements survenus lors de l'évaluation.
Il est aussi possible d'enregistrer un Listener de type Consumer<SnippetEvent> en utilisant la méthode onSnippetEvent() pour exploiter les événements émis lors des évaluations de fragments.
Exemple ( code Java 9 ) : |
import java.util.List;
import jdk.jshell.JShell;
import jdk.jshell.Snippet;
import jdk.jshell.SnippetEvent;
public class TestJShell {
public static void main(String[] args) {
List<String> fragments = List.of("5",
"int i=10;",
"System.out.println(i);",
"long ajouter(int a, int b) { return a+b;}",
"ajouter(i,$1);","ii++;");
try (JShell shell = JShell.create()) {
shell.onSnippetEvent(TestJShell::afficherSnippetEvent);
for (String fragment : fragments) {
shell.eval(fragment);
}
}
}
private static void afficherSnippetEvent(SnippetEvent se) {
Snippet snippet = se.snippet();
System.out.println("snippet event");
System.out.println(" status : " + se.status());
System.out.println(" value : " + se.value());
System.out.println(" snippet");
System.out.println(" id : " + snippet.id());
System.out.println(" source : " + snippet.source());
System.out.println(" kind : " + snippet.kind());
System.out.println(" subkind : " + snippet.subKind());
}
}
Résultat : |
snippet event
status : VALID
value : 5
snippet
id : 1
source : 5
kind : VAR
subkind : TEMP_VAR_EXPRESSION_SUBKIND
snippet event
status : VALID
value : 10
snippet
id : 2
source : int i=10;
kind : VAR
subkind : VAR_DECLARATION_WITH_INITIALIZER_SUBKIND
10
snippet event
status : VALID
value :
snippet
id : 3
source : System.out.println(i);
kind : STATEMENT
subkind : STATEMENT_SUBKIND
snippet event
status : VALID
value : null
snippet
id : 4
source : long ajouter(int a, int b) { return a+b;}
kind : METHOD
subkind : METHOD_SUBKIND
snippet event
status : VALID
value : 15
snippet
id : 5
source : ajouter(i,$1);
kind : VAR
subkind : TEMP_VAR_EXPRESSION_SUBKIND
snippet event
status : REJECTED
value : null
snippet
id : 6
source : ii++;
kind : ERRONEOUS
subkind : UNKNOWN_SUBKIND
Développons en Java v 2.40 Copyright (C) 1999-2023 Jean-Michel DOUDOUX. |