有没有一种方法可以将代码存储为Java中的变量?

时间:2020-07-17 21:33:30

标签: java oop variables

在Java中,是否有一种方法可以存储,编辑,转换,打印,访问,评估和比较代码块(可能是用户输入的),同时还能够执行它们?

一个有用的例子是,某人编写了一个旨在教人们如何编码的软件,用户将代码输入系统,程序将检查用户开发的代码是否是

我正在寻找这样的东西:

CodeBlock line20And21 = `String x = "hello"; System.out.println(x);`; // stores a block of code
line20And21.replace("ln",""); //edits the block of code
System.out.println(line20And21.toString()); // converts/prints the block of code
CodeBlock usersCode = Scanner.nextCodeBlock(); // accesses block of code
if(! line20And21.wouldThrowError()); // evaluates block of code
    if(line20And21.wouldDoTheSameThingAs(line18And19)) // compares blocks of code
        line20And21.execute(); // executes the block of code

我要使用的代码当然比定义一个字符串并打印它要复杂得多,但是我敢肯定,想法是一样的。 我真的希望对此有所帮助。谢谢!

3 个答案:

答案 0 :(得分:2)

自Java 9开始,Java包含一个用于评估代码片段的外壳,称为JShell。可通过jdk.shell以编程方式获得JShell。

首先,您必须通过JShell js = JShell.create()创建JShell的实例。
通过js.eval("System.out.println(/"Hello World/")");将字符串评估为Java代码(即所谓的代码段)是完成的,它返回SnippetEvent的列表,您可以检查这些列表以了解代码段的执行效果。 / p>

由于代码存储为字符串,因此您可以像编辑任何字符串一样对其进行编辑。

这里是一个JShell的示例,它从official java docs中获取用户输入的代码并对其进行存储/评估,其中代码从stdin中作为字符串读取并执行:

 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");
     }
 }

答案 1 :(得分:1)

您可以使用BeanShell(用Java编写的Java解释器)来执行以下操作:

import bsh.Interpreter;

class Test {
  public static void main(String[] args) throws Exception {
    String code = "String x = \"hello\"; System.out.println(x);";
    String newCode = code.replace("ln", "");

    System.out.println("Here's the result of running: " +newCode);

    Interpreter p = new Interpreter();
    p.eval(newCode);
  }
}

如果使用正确的依赖项进行编译和构建,则可以评估代码段:

$ javac -cp bsh-2.0b4.jar:. Test.java && java -cp bsh-2.0b4.jar:. Test
Here's the result of running: String x = "hello"; System.out.print(x);
hello$

您可以运行代码并获取其输出或返回值,或者是否抛出异常。沙盒测试和比较两个摘要的输出由您决定。

答案 2 :(得分:-2)

大多数:否。

Java代码经过一个编译步骤,代码需要放在方法中(需要在类型中),其原因需要花相当长的时间才能解释。编译器不需要在运行时在那里(通常不是)。

因此,如果您真的想这样做:

  1. 代码必须是“完整的”,包括包装声明,类声明,方法等。
  2. 您需要将编译器与应用程序一起交付。对于javac,这很棘手(GPL);您可以交付MIT许可的ecj(eclipse的编译器)。真是个好主意。
  3. 然后您可以将代码“存储”为字符串。
  4. ecj然后可以为您将其转换为字节码,但是您将有足够的时间正确管理类路径以使此代码正确编译。
  5. 然后,您可以使用一个类加载器来动态加载该字节码,该类加载器本身就需要几天的学习时间。

此外,在没有执行代码的情况下查看代码以确定它是否会引发异常从根本上是不可能的-这被称为暂停问题。有证据表明这是无法解决的。你走得比光还快。您无法从以“图灵完成”语言编写的任意代码(并且Java正在完成)中确定它是否停止(或者在这种情况下,是否抛出)。

类似的规则适用于“这是否会做与...相同的事情”。