IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

 

Développons en Java   2.30  
Copyright (C) 1999-2022 Jean-Michel DOUDOUX    (date de publication : 15/06/2022)

[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

 

96. JShell

 

chapitre    9 6

 

Niveau : niveau 1 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 :

 

96.1. Les outils de type REPL

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 saisir des expressions ou des traitements (Read)
  • de les évaluer une fois la touche Entrée pressée (Eval)
  • d'afficher le résultat (Print)
  • et de recommencer (Loop)

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.

 

96.1.1. L'utilité d'un outil de type REPL

Un outil de type REPL peut avoir de nombreuses utilisations :

  • expérimenter du code
  • explorer une API
  • faciliter la découverte du langage sans avoir à utiliser un IDE notamment dans le cadre de l'enseignement ou de l'apprentissage du langage

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é.

 

96.1.2. REPL vs IDE

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.

 

96.2. Introduction à JShell

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.

 

96.2.1. Les intérêts à utiliser JShell

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 :

  • l'écriture du code dans un fichier,
  • la compilation du fichier source pour créer un (ou plusieurs) fichier .class (en corrigeant les erreurs aux besoins),
  • l'exécution dans une JVM de ce fichier .class éventuellement en mode debug,
  • la réalisation de tests,
  • selon le résultat des tests, itérer sur ce processus jusqu'à satisfaction

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 :

  • faciliter l'apprentissage du langage Java grâce à son mode de fonctionnement
  • développer rapidement des prototypes
  • explorer l'utilisation et l'expérimentation d'une nouvelle API ou d'une bibliothèque : Java propose dans le JDK de nombreuses API et il existe un nombre encore plus important d'API open source
  • tester rapidement des portions de code : par exemple pour valider un algorithme ou un prototype
  • réaliser des démos ou des exercices

 

96.2.2. L'implémentation de JShell

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 :

  • l'état est inclus dans une instance de JVM
  • l'API Compiler permet de créer le byte code du code exécuté avec quelques sous-classes prenant en charge les fragments qui ne sont pas du code Java standard
  • l'API Java Debug Interface (JDI) est utilisée pour remplacer du code existant

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.

 

96.2.3. Lancer et arrêter JShell

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.

 

96.2.4. La mise en oeuvre de JShell

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 :

  • il n'a pas été utile de créer une classe ni une méthode main()
  • l'instruction saisie n'a pas l'obligation de se terminer par un point-virgule

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 :

  • l'évaluation d'expression et de déclarations sans avoir à les inclure dans une méthode
  • la déclaration de variables sans avoir à les inclure dans une classe ou une méthode
  • la définition de méthodes sans avoir à les inclure dans une classe ou une interface

Cela signifie, par exemple, qu'il est possible d'exécuter quelques lignes de Java sans avoir à écrire une classe ou une méthode.

 

96.3. La saisie d'éléments dans JShell

JShell permet la saisie de deux types d'éléments :

  • des fragments (snippets) de code Java
  • des commandes (commands) qui commencent par un caractère slash

JShell propose aussi de nombreuses fonctionnalités pour faciliter leur saisie :

  • maintient un historique,
  • possède un éditeur intégré simple,
  • complétion en utilisant la touche tab,
  • propose des commandes pour sauver et charger du code, quitter, ...
  • ajoute automatiquement un point-virgule,
  • ...

 

96.3.1. Les fonctionnalités de saisie

Un fragment (snippet) correspond à un des éléments syntaxiques du Java Language Specification (JLS) :

  • une valeur primitive
  • une expression
  • une instruction
  • la déclaration d'un champ
  • la déclaration d'une méthode
  • la déclaration d'une classe, d'une interface, d'une énumération ou d'un record
  • la déclaration d'un import

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 :

  • les variables et méthodes sont associées comme membre static d'une classe dédiée
  • les expressions et les instructions sont associées à une méthode static d'une classe dédiée

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é :

  • le nom pour une variable ou une classe
  • la signature pour une méthode (pour supporter la surcharge)

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.

 

96.3.2. Le résultat de l'évaluation d'un fragment

Le résultat d'une évaluation correcte d'un fragment peut prendre trois valeurs :

  • added (ajouté) : c'est la première déclaration de l'élément
  • modified (modifié) : l'élément existe mais sa signature reste inchangée. Dans ce cas, aucun autre fragment n'est impacté
  • replaced (remplacé) : l'élément existe mais sa signature a changé

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.

 

96.4. Les fragments

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 :

  • la définition de variables,
  • des expressions,
  • des traitements,
  • des imports,
  • des types,
  • des méthodes

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.

 

96.4.1. Les variables et les expressions

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

 

96.4.2. Les instructions de contrôle de flux

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;
|  ^----^

 

96.4.3. La définition et l'invocation de méthodes

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() {}
|  ^----------^

 

96.4.4. La création de classes et d'instances

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)
|                     ^--^

 

96.5. Les commandes de JShell

JShell propose plusieurs commandes pour :

  • obtenir des informations sur la session,
  • interagir avec l'environnement,
  • configurer l'environnement

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.

 

96.5.1. La commande /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
...

 

96.5.2. La commande /exit

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>

 

96.5.3. La commande /list

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 :

  • sNN pour des fragments issus des scripts de démarrage
  • eNN pour de fragments dont l'évaluation à générer une erreur
  • NN pour les fragments dont l'évaluation à réussie
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.

 

96.5.4. 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

 

96.5.5. La commande /imports

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.*

 

96.5.6. La commande /vars

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.

 

96.5.7. La commande /methods

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.

 

96.5.8. La commande /types

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.

 

96.5.9. La commande /save

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.

 

96.5.10. La commande /open

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 :

  • DEFAULT : contient les imports par défaut
  • PRINTING : contient des surcharges des méthodes print(), println() et printf() pour faciliter l'affichage d'informations
  • JAVASE : contient les imports de tous les packages de Java SE
Résultat :
jshell> /open PRINTING
jshell> println("coucou")
coucou

 

96.5.11. La commande /edit

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.

 

96.5.12. La commande /drop

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); }

 

96.5.13. La commande /<id>, /! et /-<n>

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

 

96.5.14. La commande /set

Elle permet de configurer l'environnement de JShell.

 

96.5.14.1. L'option editor

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

 

96.5.14.2. L'option start

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); }
...

 

96.5.15. L'achèvement des commandes

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

 

96.5.16. L'abréviation des commandes

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.

 

96.6. L'édition

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 :

  • Ctrl
  • Meta : la touche alt sur PC
  • Shift

 

96.6.1. La navigation dans la ligne courante

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
Ctrl+B

Se déplacer d'un caractère à gauche

Flèche droite
Ctrl+F

Se déplacer d'un caractère à droite

Retour chariot (Return)

Valider la ligne courante

Home
Ctrl+A

Se déplacer au début de la ligne

Fin (End)
Ctrl+E

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.

 

96.6.2. La navigation dans l'historique

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
Ctrl+B

Remplacer la ligne courante par le contenu de la ligne précédente dans l'historique

Flèche bas
Ctrl+B

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.

 

96.6.3. La modification de la ligne courante

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.

 

96.6.4. La recherche et les autres fonctionnalités

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 :

  • Ctrl+R : pour rechercher l'élément précédent en remontant dans l'historique
  • Ctrl+S : pour rechercher l'élément suivant en descendant dans l'historique

 

96.6.5. La définition et l'utilisation d'une macro

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.

 

96.6.6. L'utilisation d'un éditeur

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.

  • JSHELLEDITOR
  • VISUAL
  • EDITOR

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.

 

96.7. Les fonctionnalités de l'environnement

JShell propose des fonctionnalités pour faciliter la saisie du code.

 

96.7.1. L'exploration des API et l'achèvement de 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(

 

96.7.2. Les références à venir (Forward references)

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

 

96.7.3. La redéclaration d'une variable ou d'une méthode

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);
       }

 

96.7.4. Les exceptions de type checked

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)

 

96.7.5. L'ajout d'un import

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.
 

 

96.7.6. La création d'une variable à partir d'une expression

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

 

96.8. Les scripts

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.

 

96.8.1. Les scripts de démarrage

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
 

 

96.8.2. La création d'un script

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>

 

96.8.3. Le chargement d'un script

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>

 

96.9. Les modes de feedback

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.

 

96.9.1. L'utilisation d'un mode de feedback

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.

 

96.9.2. La définition d'un mode de feedback

Il est possible de définir son propre mode de feedback :

  • le format de l'invite (prompt)
  • le niveau d'informations que JShell affichera à la saisie de différents éléments

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

  • Prompts (le format des invites) : normal (regular) et de suite de fragment (continuation)
  • Truncation : la taille maximale des valeurs affichées
  • Format (de retour) : le niveau d'informations que JShell fournit

 

96.9.2.1. La définition des prompts

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>

 

96.9.2.2. La troncature des valeurs affichées

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.

 

96.9.2.3. Le format du retour de l'évaluation

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.

 

96.10. L'utilisation de classes externes

Il est possible d'ajouter des classes via le classpath ou le modulepath pour être utilisées dans la session de JShell.

 

96.10.1. La configuration du classpath

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.

 

96.10.2. La configuration du modulepath

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

 

96.11. Les options de JShell

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

 

96.12. JShell API

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 :

  • jdk.shell
  • jdk.shell.spi
  • jdk.shell.execution
  • jdk.shell.tool

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

 

 


[ Précédent ] [ Sommaire ] [ Suivant ] [Télécharger ]      [Accueil ]

78 commentaires Donner une note à l´article (5)

 

Copyright (C) 1999-2022 Jean-Michel DOUDOUX. Vous pouvez copier, redistribuer et/ou modifier ce document selon les termes de la Licence de Documentation Libre GNU, Version 1.1 ou toute autre version ultérieure publiée par la Free Software Foundation; les Sections Invariantes étant constitués du chapitre Préambule, aucun Texte de Première de Couverture, et aucun Texte de Quatrième de Couverture. Une copie de la licence est incluse dans la section GNU FreeDocumentation Licence. La version la plus récente de cette licence est disponible à l'adresse : GNU Free Documentation Licence.