○JShellとは

JShellは、Javaのコードを1行ずつ対話的に実行することができるコマンドラインツールで、Java 9以降のJDKに標準で付属するようになった。いわゆる「REPL(Read-Evaluate-Print-Loop)」と呼ばれるツールの一種で、プログラムのコードを入力すると、その時点で宣言や文、式が評価され、ただちに結果が出力される。

Javaのプログラムを動作させるには、通常はimport宣言やクラス宣言などを含むコードをすべて記述した上で、コンパイルしてclassファイルを作成し、それを実行するという一連の手続きが必要となる。数行程度のコードの挙動を確認したり、使ったことのないAPIをちょっと試してみたりしたいだけなのに、すべての宣言を含んだ完全なプログラムをいちいち作らなくてはいけないことを面倒に感じることもあるだろう。

JShellを使えば、試したい部分のコードだけを入力・実行してすぐに結果を確認できるので、そのような煩わしさを感じずに済む。入力内容を調整したい場合も、再コンパイルなしですぐに実行できる。

○基本的な操作

JShellはJava 9以降のJDKで利用できる。JDKをインストールすれば、javaやjavacコマンドと同じディレクトリに格納されているので、これらのコマンドのディレクトリに実行パスが通っていれば、コマンドラインから下記のようにjshellコマンドを実行することで起動できる。

$ jshell
| JShellへようこそ -- バージョン13.0.1
| 概要については、次を入力してください: /help intro

jshell>

JShellは対話式のツールなので、起動したらそこにJavaのコードを書いていけば、即座に実行されて結果が返ってくる。たとえば恒例の「Hello World!」は次のようになる。

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

通常のJavaプログラムと違って行末のセミコロン(;)は省略できる。Enterキーを押した時に、入力済みのコードが完成の形になっていれば、JShellがそれをプログラムの式だと解釈して実行してくれるからだ。もし中途半端な位置でエンターキーが入力された場合には、改行されて続きの入力が求められる。

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

なお、IDEのエディタのようにタブによる入力補完機能を備えているので、たとえば「System」であれば「Sys」あたりまで入力してタブキーを押せば残りは自動で補完してくれる。また、上キーを押せば以前に入力した内容を遡ることができる。

JShellに入力した内容は、原則としてJavaのコードとして解釈される。それとは別にJShellそのものを操作するためのコマンドが用意されており、それらのコマンドはスラッシュ(/)の後に続けて入力する。たとえば、JShellを終了するためのコマンドは「/exit」である。

jshell> /exit
| 終了します
$

○演算と変数

JShellでは、次のように式を直接入力しただけでもちゃんと結果を出力してくれる。

jshell> 1 + 2 * 3
$2 ==> 7

この場合、計算結果は$2という一時的に変数に格納される。そこで、次のように「$2」と入力すると、その中身を確認できる。

jshell> $2
$2 ==> 7

明示的に変数を宣言したい場合、通常のJavaプログラムのようにintやStringのような型宣言もできるが、代わりに次のように「var」を使うほうが便利である。

jshell> var num = 1 + 2
num ==> 3

jshell> num * 3
$4 ==> 9

varはJava 10から導入された新機能で、型推論によって変数の型を自動で判別してくれる。代入される値がint型であればintとして、String型であればStringとして扱ってくれるというものだ。以下の例では、textはString型になる。Stringなのでlength()メソッドが使える。さきほどのnumはint型なのでlength()を呼び出そうとするとエラーになる。

jshell> var text = "Hello!"
text ==> "Hello!"

jshell> text.length()
$6 ==> 6

jshell> num.length()
| エラー:
| intは間接参照できません
| num.length()
| ^--------^

○クラス宣言・メソッド宣言

通常のJavaプログラムと同じようにクラスの宣言ができる。例えば、2つのint型フィールドを持つPointクラスの宣言は次のようになる。

jshell> class Point {
...> int x;
...> int y;
...> }
| 次を作成しました: クラス Point

メソッドを宣言することもできる。次の例はString型の引数を取るgreeting()メソッドを宣言したものだ。

jshell> String greeting(String msg){ return "Hello " + msg + "!"; }
| 次を作成しました: メソッド greeting(String)

jshell> greeting("Java")
$15 ==> "Hello Java!"

この例のように、クラスの指定なしでメソッド宣言された場合は、JShell内部で自動的にクラスが作成され、そのstaticメソッドとして扱われるようになる。この場合、クラス名なしでの呼び出しができる。

クラスやメソッドの宣言は、後から重複する宣言を行えば、元の宣言を上書きして更新することができる。

jshell> String greeting(String msg){ return "Goodmorning " + msg + "!"; }
| 次を変更しました: メソッド greeting(String)

jshell> greeting("Java")
$18 ==> "Goodmorning Java!"

JShellのクラス宣言の特徴として、前方参照と呼ばれる仕組みがある。これは、まだ宣言されていないクラスでも、クラス宣言の中で使用することができるというものだ。次の例では、Memberクラスの宣言時にはまだIdクラスが存在しない。しかし、この時点ではエラーにはならず、Memberクラスの宣言は成功する。ただし、後からIdクラスが宣言されるまでは、このMemberクラスを参照することはできない。

jshell> class Member {
...> Id id;
...> String name;
...> }
| 次を作成しました: クラス Member。しかし、 class Idが宣言されるまで、参照できません

jshell> class Id {
...> String id;
...> }
| 次を作成しました: クラス Id

○JShellの便利なコマンド

コマンドを使いこなせば、より便利にJShellを活用できる。例えば「/list」コマンドは、これまで入力したコードを一覧表示するコマンドである。それぞれのコードにはIDが付けられている。

jshell> /list

1 : System.out.println("Hello World!")
2 : 1 + 2 * 3
3 : var num = 1 + 2;
4 : num * 3
5 : var text = "Hello!";
6 : text.length()
7 : class Point {
int x;
int y;
}
9 : greeting("Java")
10 : String greeting(String msg){ return "Goodmorning " + msg + "!"; }
11 : class Member {
Id id;
String name;
}
12 : class Id {
String id;
}

次のように、「/」に続けてIDを入力することで、該当のIDのコードを再実行することができる。再実行する対象はIDの範囲でも指定可能だ。

jshell> /1
System.out.println("Hello World!")
Hello World!

jshell> /3-6
var num = 1 + 2;
num ==> 3
num * 3
$22 ==> 9
var text = "Hello!";
text ==> "Hello!"
text.length()
$24 ==> 6

一度入力したコードの内容を少し変えて実行してみたい場合は、「/edit」コマンドを使うといい。IDを指定してこのコマンドを実行すると、JShell Editor Padというエディタが起動して、対象のコードを編集できるようになる。コードを書き換えて[Accept]を押せば、書き換えたコードが実行される。宣言を上書きしたり、ちょっとした挙動の違いを確認したりする場合に便利である。

jshell> /edit 7

そのほかにも、宣言済みのクラスやメソッド、変数などを一覧表示したり、ファイルからコードを読み込んで実行したりするためのコマンドが用意されている。利用できるコマンドの一覧は「/help」コマンドで確認できる。

JShellはそれ単体で開発を行えるというものでないが、新しい機能やAPIの検証や、ちょっとしたコードの動作確認をしたい場合には極めて便利なツールである。使いこなせば、開発効率の向上にもつながるだろう。