Module jdk.jshell

Package jdk.jshell


package jdk.jshell
提供了用于创建工具的接口,例如交互式评估“Java”编程语言代码的“Read-Eval-Print Loop(REPL)”。其中,“片段”是单个表达式、语句或声明。此功能可用于增强诸如IDE之类的工具,也可以独立使用。

JShell 是中心类。一个JShell实例保存了评估状态,即当前的源代码片段集合和它们产生的执行状态。

每个源代码片段由Snippet的子类实例表示。例如,语句由StatementSnippet的实例表示,方法声明由MethodSnippet的实例表示。当使用包含一个或多个代码片段的输入调用JShell.eval(String)时,将创建片段。

有关片段编译状态的任何更改都将使用SnippetEvent报告。片段状态的更改有三种主要类型:使用eval创建,使用JShell.drop(jdk.jshell.Snippet)从活动源状态中删除,以及由于另一个片段的状态更改而更新其状态。例如:给定js,一个JShell的实例,执行js.eval("int x = 5;")将向源状态添加变量x并生成一个描述为x创建VarSnippet的事件。然后执行js.eval("int timesx(int val) { return val * x; }")将向源状态添加一个方法并生成一个描述为timesx创建MethodSnippet的事件。假设varx保存了第一次调用eval创建的片段,执行js.drop(varx)将生成两个事件:一个用于将变量片段的状态更改为DROPPED,另一个用于更新方法片段(现在具有对x的未解析引用)。

当然,对于API的任何一般应用程序,输入不会是固定的字符串,而是来自用户。下面是一个非常简化的示例,展示了如何使用API实现REPL。

 
     import java.io.ByteArrayInputStream;
     import java.io.Console;
     import java.util.List;
     import jdk.jshell.*;
     import jdk.jshell.Snippet.Status;

     class ExampleJShell {
         public static void main(String[] args) {
             Console console = System.console();
             try (JShell js = JShell.create()) {
                 do {
                     System.out.print("Enter some Java code: ");
                     String input = console.readLine();
                     if (input == null) {
                         break;
                     }
                     List<SnippetEvent> events = js.eval(input);
                     for (SnippetEvent e : events) {
                         StringBuilder sb = new StringBuilder();
                         if (e.causeSnippet() == null) {
                             //  We have a snippet creation event
                             switch (e.status()) {
                                 case VALID:
                                     sb.append("Successful ");
                                     break;
                                 case RECOVERABLE_DEFINED:
                                     sb.append("With unresolved references ");
                                     break;
                                 case RECOVERABLE_NOT_DEFINED:
                                     sb.append("Possibly reparable, failed  ");
                                     break;
                                 case REJECTED:
                                     sb.append("Failed ");
                                     break;
                             }
                             if (e.previousStatus() == Status.NONEXISTENT) {
                                 sb.append("addition");
                             } else {
                                 sb.append("modification");
                             }
                             sb.append(" of ");
                             sb.append(e.snippet().source());
                             System.out.println(sb);
                             if (e.value() != null) {
                                 System.out.printf("Value is: %s\n", e.value());
                             }
                             System.out.flush();
                         }
                     }
                 } while (true);
             }
             System.out.println("\nGoodbye");
         }
     }
 
 

要注册状态更改事件,请使用JShell.onSnippetEvent(java.util.function.Consumer)。这些事件仅由evaldrop生成,这些方法的返回值是该调用生成的事件列表。因此,就像上面的示例一样,可以在不注册接收事件的情况下使用事件。

如果您尝试此示例,您将看到未能使用分号终止语句或变量声明将导致失败。未完成的条目(例如期望的多行方法)也将在一行后失败。在这种情况下,SourceCodeAnalysis中的实用程序提供源边界和完整性分析,以解决这类情况。 SourceCodeAnalysis还提供了输入的建议完成,就像在制表完成中使用的那样。

自版本:
9