Java SE 9 の予習 その4
Java SE 9 の予習 その4です。
前回のエントリーの続きでおそらく今回も必要とされない無駄なお遊びとなります。
今回は JShell API を利用してアプリケーション内でスニペットの評価や実行を試してみます。
なお、今回のエントリーには 「Java SE 9のProject Kulla、JShellの動作とJShell API (3/4)(JShell API)」の記事を参考にさせていただきました。
ほとんどプログラムは丸写し状態になっていますので元記事をまず読んで理解してこのお遊びプログラムを堪能していただければ幸いです。
下記プログラムは普通に19行目のコンストラクタ内で JShell.create() メソッドによりオブジェクトを生成します。
スニペットの評価実行は29行目の eval(String snipetSource) メソッドでおこなわれます。(折り畳んでいるので隠れてます。)
( 最後にいろいろゴニョゴニョ遊んだコードを載せますので行番号はずれてしまいますが参考にしてくださいませ。)
List<SnippetEvent> eval(String snipetSource) メソッドで SnippetEvent オブジェクトを取得します。
JavaDoc では 戻り値:この評価によって直接的または間接的に発生したイベントのリスト となっています。
つまり、ステータスや値です。
種類は、jdk.jshell.Snippet クラスの public Kind kind() メソッドで取得します。
戻り値である jdk.jshell.Snippet.Kind は jdk.jshell.Snippet のサブクラスを示します。
jdk.jshell.Snippet.Kind は jdk.jshell.Snippet クラスの public static enum Kind extends Enum<Kind> となっています。
つまり種類は jdk.jshell.Snippet クラスのイミュータブルプロパティです。
jdk.jshell.Snippetクラスには 種類を補足するための public SubKind subKind() メソッドも用意されています。
これは jdk.jshell.Snippet クラスの public static enum SubKind extends Enum<SubKind> を返します。
これはユーザーへのフィードバックに役立ちます。
これは種類の詳細な情報を取得するのに利用されます。
ステータスの取得には jdk.jshell.SnippetEvent クラスの public Status status() メソッドを使用します。
戻り値は jdk.jshell.Snippet クラスの public static enum Status extends Enum<Status> です。
これは JShell ステート内の動的プロパティです。
したがって JShell クエリで取得されます。
ステータスには次の7つの状態があります。
DROPPED:スニペットは、JShell.drop(Snippet)への明示的な呼び出しのために非アクティブです。
NONEXISTENT:スニペットはまだ存在しないため、非アクティブです。
OVERWRITTEN : 新しいスニペットに置き換えられているため、このスニペットは無効です。
RECOVERABLE_DEFINED:スニペットは、(現在のJShell状態のコンテキストで)回復可能な未解決の参照やその他の問題が本体内にある可能性のある宣言スニペットです。
RECOVERABLE_NOT_DEFINED:スニペットは、(現在のJShell状態のコンテキストで)回復可能な未解決の参照やその他の問題がある可能性のある宣言スニペットです。
REJECTED:スニペットは、初期評価時にコンパイルに失敗し、JShell状態のさらなる変更で有効になることができないため、非アクティブです。
VALID:スニペットは有効なスニペット(現在のJShell状態のコンテキスト内)です。
このプログラムではステータスが VALID でなければ jdk.jshell.JShell クラスの public Stream<Diag> diagnostics(Snippet snippet) メソッドでスニペットの最新評価の診断を取得します。
そして jdk.jshell.Diag クラスの public abstract String getMessage(Locale locale) メソッドで指定されたロケールのローカライズされたメッセージを返します。
実際のメッセージは実装依存です。 ロケールがnullの場合、デフォルトのロケールを使用します。
値の取得には、jdk.jshell.SnippetEvent クラスの public String value() メソッドを使います。
戻り値は成功した実行の結果値。
実行されなかった場合、または例外がスローされた場合、値はnullです。
以上、だいたいこんな感じで JShell API を楽しんでいきたいと思います。
動かすスニペットは例の「よく見かけるネタ IntegerCache 問題」です。
これだけだとさみしいので FunctionalInterface を利用した挨拶プログラムも JShell API で動かしてみましょう。
非常にシンプルなスニペットの実行となります。
Import statement、FunctionalInterface、などのステータス、値、種類などの情報を正確に取得できれば OK です。
では、このプログラムの実行結果を見てみましょう。
FunctionalInterface は、ただの Interface として識別されるようです。
IntegerCache 問題 もちゃんと false となっています。
これだと普通すぎて面白くないですよね。
日本ではもうすぐ Java SE 9 がリリースされようという時代に End Of Life となった Java SE 7 でプロフラムを組まなければいけない
涙無くしては語れない非常に過酷な環境に置かれているプログラマが多く存在するらしい。
そこでそういう可愛そうなプログラマのためにコンパイラオプションを JDK7 にし、
IntegerCache 問題で遊ぶために JVM オプションに -XX:AutoBoxCacheMax=128 を設定して実行してみます。
JShell オブジェクトの生成を JShell.create() メソッドから JShell.builder() メソッドに変更します。
コンパイラオプションの設定は
jdk.jshell.JShell.Builder クラスの public Builder compilerOptions(String… options) メソッドと使い設定します。
可変長引数の使い方が独特で思いっきりドツボにはまったのは内緒です。
JVM オプションの設定は
jdk.jshell.JShell.Builder クラスの public Builder remoteVMOptions(String… options) メソッドを使い設定します。
後は入出力の設定です。
これらは連鎖初期化で使用するため Builderインスタンスを返します。
最後に jdk.jshell.JShell.Builder クラスの public JShell build() メソッドで JShell ステートエンジンを構築します。
これはすべてのJShell機能のエントリーポイントです。
これにより、実行のためのリモートプロセスが作成されます。
それではコンパイラオプションと JVM オプションが正しく機能するかプログラムを走らせてみましょう。
うわぁぁ!!
正しく期待通りの動作となったが何故か目から涙がこぼれそうに・・・
IntegerCache 問題の実行結果も true となり正しく JVM オプションが設定されたようです。
これだけで天の邪鬼な私のお遊びは終了です。
ここから先は思い込みでサプライズさせてもらったお話しです。
先のプログラムにコンパイラオプションと JVM オプションを設定しない元の状態に戻して下記スニペットの実行コードを追加してみました。
JShell では import 文を必要としないパッケージが何故か存在します。
java.util.Arrays もその一つだから無事にプログラムは実行されるだろうと思っていました。
((((;゜Д゜))))))) なんと! ERROR! EXPRESSION [ シンボルを見つけられません・・・ ]
JShell API を利用する場合はおもてなしは無しなのか!?
しかたないから import 文を追記してみた。
今度は期待通りの動作で実行された。
ついでだから enum も試してみた。
ちゃんと enum 認識していた。
実は Linux 環境の NetBeans IDE だからちゃんと日本語対応されているのですが
Windows 環境だと日本語は文字化けしてしまいます。
セレブデベロッパーご愛用の IntelliJ IDEA (2017.2 EAP)でも Windows環境での JShellAPI で日本語は文字化けしてしまう。
これは Java SE 9 リリースまでに修正されるのだろうか?
ちなみに Windows のコマンドプロンプトからの実行は文字化けしません。
以上、JShell API を無駄に使って遊んでみたエントリーでした。
お終い!
おっと!JShell API と戯れた記念コードを載せておきますね。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
package jp.yucchi.jshellapiexample; import java.util.List; import java.util.Locale; import jdk.jshell.ErroneousSnippet; import jdk.jshell.ExpressionSnippet; import jdk.jshell.JShell; import jdk.jshell.MethodSnippet; import jdk.jshell.Snippet; import jdk.jshell.Snippet.Status; import jdk.jshell.SnippetEvent; import jdk.jshell.TypeDeclSnippet; import jdk.jshell.VarSnippet; public class JShellAPIExample { private final JShell shell; public JShellAPIExample() { shell = JShell.create(); // shell = JShell.builder() // .compilerOptions("-source", "1.7", "-target", "1.7", // "-bootclasspath", "/home/yucchi/Sun/jdk1.7.0_80/jre/lib/rt.jar", // "-extdirs", "") // .remoteVMOptions("-XX:AutoBoxCacheMax=128") // .in(System.in) // .err(System.err) // .out(System.out) // .build(); } public void close() { if (shell != null) { shell.close(); } } public void eval(String snippetSource) { System.out.println(snippetSource); List<SnippetEvent> se = shell.eval(snippetSource); se.forEach(e -> { if (e.causeSnippet() == null) { Snippet snippet = e.snippet(); switch (snippet.kind()) { case IMPORT: switch (snippet.subKind()) { case SINGLE_TYPE_IMPORT_SUBKIND: System.out.println("<< Single-Type-Import Declaration. >>"); break; case SINGLE_STATIC_IMPORT_SUBKIND: System.out.println("<< Single-Static-Import Declaration. >>"); break; case TYPE_IMPORT_ON_DEMAND_SUBKIND: System.out.println("<< Type-Import-on-Demand Declaration. >>"); break; case STATIC_IMPORT_ON_DEMAND_SUBKIND: System.out.println("<< Static-Import-on-Demand Declaration. >>"); break; default: break; } break; case ERRONEOUS: System.out.println("ERROR! " + ((ErroneousSnippet) snippet).probableKind() + ":"); break; case TYPE_DECL: TypeDeclSnippet type = (TypeDeclSnippet) snippet; System.out.print("<< Created "); switch (snippet.subKind()) { case CLASS_SUBKIND: System.out.print("Class: "); break; case INTERFACE_SUBKIND: System.out.print("Interface: "); break; case ENUM_SUBKIND: System.out.print("Enum: "); break; case ANNOTATION_TYPE_SUBKIND: System.out.print("Annotation: "); break; default: break; } System.out.println(type.name() + " >>"); break; case METHOD: MethodSnippet method = (MethodSnippet) snippet; System.out.println("<< Created Method: " + method.name() + "(" + method.parameterTypes() + ")" + " >>"); break; case VAR: switch (snippet.subKind()) { case VAR_DECLARATION_SUBKIND: System.out.println("<< A variable declaration without initializer. >>"); System.out.println(((VarSnippet) snippet).name() + " ==> " + e.value()); break; case TEMP_VAR_EXPRESSION_SUBKIND: System.out.println("<< An expression whose value has been stored in a temporary variable. >>"); System.out.println(((VarSnippet) snippet).name() + " ==> " + e.value()); break; case VAR_DECLARATION_WITH_INITIALIZER_SUBKIND: System.out.println("<< A variable declaration with an initializer expression. >>"); System.out.println(((VarSnippet) snippet).name() + " ==> " + e.value()); break; default: break; } break; case EXPRESSION: switch (snippet.subKind()) { case VAR_VALUE_SUBKIND: System.out.println("<< A simple variable reference expression. >>"); System.out.println(((ExpressionSnippet) snippet).name() + " ==> " + e.value()); break; case ASSIGNMENT_SUBKIND: System.out.println("<< An assignment expression. >>"); System.out.println(((ExpressionSnippet) snippet).name() + " ==> " + e.value()); break; default: break; } break; case STATEMENT: System.out.println("<< " + "Statement: " + e.snippet() + " >>"); break; default: System.out.println("<< " + e.getClass().getCanonicalName() + " >>"); break; } if (e.status() != Status.VALID) { this.shell.diagnostics(e.snippet()).forEach(i -> { System.out.println("[ " + i.getMessage(Locale.getDefault()) + " ]"); }); } } }); } public static void main(String[] args) throws Exception { JShellAPIExample example = new JShellAPIExample(); example.eval("import java.time.LocalTime;"); example.eval("@FunctionalInterface \n" + "interface Greeting<T, R> {\n" + "R apply(T time, R message);\n" + "\n" + " default void sayGreeting(String greeting) {\n" + " greeting(greeting);\n" + " }\n" + "\n" + " private void greeting(String greeting) {\n" + " System.out.println(greeting + \" from JShellAPI.\");\n" + " };\n" + "}"); example.eval("LocalTime now = LocalTime.now();"); example.eval("int hour = now.getHour();"); example.eval("Greeting<Integer, String> greeting\n" + " = (time, message) -> 4 <= time && time < 11 ? \"Good morning!\"\n" + " : 11 <= time && time < 18 ? \"Good afternoon!\"\n" + " : \"Good evening!\";"); example.eval("String message = null;"); example.eval("greeting.sayGreeting(greeting.apply(hour, message));"); example.eval(""); System.out.println("(○・ω・)ノ----------------------------------------------------------)\n"); example.eval("Integer x = 128;"); example.eval("Integer y = 128;"); example.eval("void equivalenceTest(Integer x, Integer y) { System.out.println(x == y); }"); example.eval("equivalenceTest(x, y);"); System.out.println("(○・ω・)ノ----------------------------------------------------------)\n"); example.eval("import java.util.Arrays;"); // 省略不可 example.eval("String str;"); example.eval("str = \"Java\";"); example.eval("String[] elems = str.split(\"\");"); example.eval("System.out.println(Arrays.toString(elems));"); example.eval("Arrays.stream(elems).forEach(System.out::println);"); System.out.println("(○・ω・)ノ----------------------------------------------------------)\n"); example.eval("public enum JavaPlusYou {\n" + "\n" + " Japanese(\"あなたとJAVA,今すぐダウンロード\"),\n" + " English(\"JAVA+YOU, DOWNLOAD TODAY!\");\n" + "\n" + " private final String downloadNow;\n" + "\n" + " private JavaPlusYou(final String downloadNow) {\n" + " this.downloadNow = downloadNow;\n" + " }\n" + "\n" + " public String getDownloadNow() {\n" + " return downloadNow;\n" + " }\n" + "\n" + "}"); example.eval("for (JavaPlusYou jpy : JavaPlusYou.values()) {\n" + " System.out.println(jpy + \": \" + jpy.getDownloadNow());\n" + " }"); System.out.println("(○・ω・)ノ----------------------------------------------------------)\n"); example.eval("System.out.println(System.getProperty(\"java.vm.version\"));"); example.eval("System.out.println(System.getProperty(\"java.class.version\"));"); System.out.println("(○・ω・)ノ----------------------------------------------------------)\n"); example.close(); } } |
実行結果は次のようになります。
run:
import java.time.LocalTime;
<< Single-Type-Import Declaration. >>
@FunctionalInterface
interface Greeting<T, R> {
R apply(T time, R message);
default void sayGreeting(String greeting) {
greeting(greeting);
}
private void greeting(String greeting) {
System.out.println(greeting + ” from JShellAPI.”);
};
}
<< Created Interface: Greeting >>
LocalTime now = LocalTime.now();
<< A variable declaration with an initializer expression. >>
now ==> 17:28:24.474604
int hour = now.getHour();
<< A variable declaration with an initializer expression. >>
hour ==> 17
Greeting<Integer, String> greeting
= (time, message) -> 4 <= time && time < 11 ? “Good morning!”
: 11 <= time && time < 18 ? “Good afternoon!”
: “Good evening!”;
<< A variable declaration with an initializer expression. >>
greeting ==> $Lambda$17/555826066@a67c67e
String message = null;
<< A variable declaration with an initializer expression. >>
message ==> null
greeting.sayGreeting(greeting.apply(hour, message));
Good afternoon! from JShellAPI.
<< Statement: Snippet:StatementKey#7-greeting.sayGreeting(greeting.apply(hour, message)); >>
(○・ω・)ノ———————————————————-)
Integer x = 128;
<< A variable declaration with an initializer expression. >>
x ==> 128
Integer y = 128;
<< A variable declaration with an initializer expression. >>
y ==> 128
void equivalenceTest(Integer x, Integer y) { System.out.println(x == y); }
<< Created Method: equivalenceTest(Integer,Integer) >>
equivalenceTest(x, y);
false
<< Statement: Snippet:StatementKey#11-equivalenceTest(x, y); >>
(○・ω・)ノ———————————————————-)
import java.util.Arrays;
<< Single-Type-Import Declaration. >>
String str;
<< A variable declaration without initializer. >>
str ==> null
str = “Java”;
<< An assignment expression. >>
str ==> “Java”
String[] elems = str.split(“”);
<< A variable declaration with an initializer expression. >>
elems ==> String[4] { “J”, “a”, “v”, “a” }
System.out.println(Arrays.toString(elems));
[J, a, v, a]
<< Statement: Snippet:StatementKey#16-System.out.println(Arrays.toString(elems)); >>
Arrays.stream(elems).forEach(System.out::println);
J
a
v
a
<< Statement: Snippet:StatementKey#17-Arrays.stream(elems).forEach(System.out::println); >>
(○・ω・)ノ———————————————————-)
public enum JavaPlusYou {
Japanese(“あなたとJAVA,今すぐダウンロード”),
English(“JAVA+YOU, DOWNLOAD TODAY!”);
private final String downloadNow;
private JavaPlusYou(final String downloadNow) {
this.downloadNow = downloadNow;
}
public String getDownloadNow() {
return downloadNow;
}
}
<< Created Enum: JavaPlusYou >>
for (JavaPlusYou jpy : JavaPlusYou.values()) {
System.out.println(jpy + “: ” + jpy.getDownloadNow());
}
Japanese: あなたとJAVA,今すぐダウンロード
English: JAVA+YOU, DOWNLOAD TODAY!
<< Statement: Snippet:StatementKey#19-for (JavaPlusYou jpy : JavaPlusYou.values()) {
System.out.println(jpy + “: ” + jpy.getDownloadNow());
} >>
(○・ω・)ノ———————————————————-)
System.out.println(System.getProperty(“java.vm.version”));
9-ea+169
<< Statement: Snippet:StatementKey#20-System.out.println(System.getProperty(“java.vm.version”)); >>
System.out.println(System.getProperty(“java.class.version”));
53.0
<< Statement: Snippet:StatementKey#21-System.out.println(System.getProperty(“java.class.version”)); >>
(○・ω・)ノ———————————————————-)
ビルド成功(合計時間: 4秒)
Trackback URL