S-JIS[2018-02-02/2018-02-03] 変更履歴
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