S-JIS[2018-02-02/2018-02-03] 変更履歴

JShell内部構造

JShellの内部構造について。

 

概要

JShellで入力したスニペットはJShell内部で保持される。

式を実行した結果はJShell内部でクラスが作られ、そのstaticフィールドになる。
関数を定義した場合はJShell内部でクラスが作られ、そのstaticメソッドになる。


内部クラスの名前

JShellの初代バージョンでは、スニペットの結果を保持するクラスは、パッケージが「REPL」でクラス名は「$JShell$数値」となる。
もっとも、JShellのバージョンが変わったら変わる可能性はあるけど。

jshell> void t() { new Exception().printStackTrace(); }
|  次を作成しました: メソッド t()

jshell> t()
java.lang.Exception
	at REPL.$JShell$11.t($JShell$11.java:4)
	at REPL.$JShell$12.do_it$($JShell$12.java:5)
	〜

実行結果の保持

JShell上でスニペット(式)を実行すると以下のようになる。

jshell> 1 + 2
$1 ==> 3

この$1は、JShell内部でクラスが作られ、そのstaticフィールドになるようだ。

クラスは、パッケージが「REPL」でクラス名は「$JShell$数値」となる。
数値は内部でスニペットを実行した順番らしい。(実行結果の変数($1)の番号ではない)
「/list -all」で実行したスニペットの一覧が表示されるが、その順番だと思われる。(by stb_nissieさんのツイート

jshell> /list -all

  s1 : import java.io.*;             	←1
  s2 : import java.math.*;           	←2
  s3 : import java.net.*;            	←3
  s4 : import java.nio.file.*;       	←4
  s5 : import java.util.*;           	←5
  s6 : import java.util.concurrent.*;	←6
  s7 : import java.util.function.*;  	←7
  s8 : import java.util.prefs.*;     	←8
  s9 : import java.util.regex.*;     	←9
 s10 : import java.util.stream.*;    	←10
   1 : 1 + 2                         	←11
jshell> REPL.$JShell$11.$1
$2 ==> 3
jshell> REPL.$JShell$11.class.getDeclaredField("$1")
$3 ==> public static int $1

REPL.」まで入力してタブキーを押すと、$Shell系のクラス一覧が表示される。
(「REP」まででタブキーを押しても補完されないので注意)

jshell> REPL.[tab]
$JShell$11   $JShell$12   $JShell$13

REPLパッケージをインポートしておくと、単純名で使える。

jshell> import REPL.*;

jshell> $JShell$11.$1
$5 ==> 3

デバッグをonにしておくと、スニペット実行時にクラス名が表示される。[2018-02-03]

jshell> /debug g
|  General debugging on

jshell> 1 + 2
Kind: EXPRESSION_STATEMENT -- 1 + 2;
compileAndLoad  [Unit($1)]
++setCompilationInfo() Snippet:VariableKey($1)#11-1 + 2
package REPL;
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.*;class $JShell$11 {
    public static
    int $1;
    public static Object do_it$() throws Throwable {
        return $1 =
        1 + 2;
    }
}

-- diags: []
setStatus() Snippet:VariableKey($1)#11-1 + 2 - status: VALID
compileAndLoad ins = [Unit($1)] -- legit = [Unit($1)]
Compiler generating class REPL.$JShell$11
compileAndLoad [Unit($1)] -- deps: []  success: true
recordCompilation: Snippet:VariableKey($1)#11-1 + 2 -- status VALID, unresolved []
$1 ==> 3

jshell>

関数の保持

JShell上で定義した関数(メソッド)は、JShell内部でクラスが作られ、そのstaticメソッドになるようだ。

このクラス名はスニペットの実行結果を保持するクラスと同じ。

jshell> String func(String s) { return s + "zzz"; }
|  次を作成しました: メソッド func(String)

jshell> func("a")
$7 ==> "azzz"

jshell> REPL.$JShell[tab]
$JShell$11   $JShell$12   $JShell$14   $JShell$15   $JShell$16   $JShell$17

jshell> REPL.$JShell$16.[tab]
class   func(

jshell> REPL.$JShell$16.func("b")
$8 ==> "bzzz"

デバッグをonにしておくと、関数定義時にクラス名が表示される。[2018-02-03]

jshell> /debug g
|  General debugging on

jshell> String func(String s) { return s + "zzz"; }
Kind: METHOD --
String func(String s) {
    return s + "zzz";
}
compileAndLoad  [Unit(func)]
++setCompilationInfo() MethodSnippet:func/(String)String-String func(String s) { return s + "zzz"; }
package REPL;
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.*;import static REPL.$JShell$11.$1;
class $JShell$12 {
    public static
    String func(String s) { return s + "zzz"; }}

-- diags: []
setStatus() MethodSnippet:func/(String)String-String func(String s) { return s + "zzz"; } - status: VALID
compileAndLoad ins = [Unit(func)] -- legit = [Unit(func)]
Compiler generating class REPL.$JShell$12
compileAndLoad [Unit(func)] -- deps: []  success: true
recordCompilation: MethodSnippet:func/(String)String-String func(String s) { return s + "zzz"; } -- status VALID, unresolved []
|  次を作成しました: メソッド func(String)

メソッド参照での利用

通常、JShell上で定義した関数に対するメソッド参照は指定できない。

jshell> Function<String, String> f = ???::func;	←ここに何を指定すればよいか?

しかし「REPL.$JShell$数値」クラスを使えばメソッド参照が出来る。

jshell> Function<String, String> f = REPL.$JShell$16::func;
f ==> $Lambda$23/1900164709@20398b7c

jshell> f.apply("c")
$10 ==> "czzz"

メソッドのジェネリクスでの利用

同様に、メソッドのジェネリクスを明示的に指定する事も出来る。

jshell> <A> A aaa(A a) { return a; }
|  次を作成しました: メソッド aaa(A)

jshell> REPL.$JShell$21.<String>aaa(null)
$12 ==> null

JShellへ戻る / Java目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま