我正在创建一个程序(可执行文件),允许用户使用图形并在其上执行算法。基本上用户应该能够创建一个新的算法,例如Dijkstra的修改,并在图表上进行尝试。但是,我不确定什么是最好的编程方式。
目前我正在考虑允许用户编写java代码并将其保存在单独的目录中。但是,我不知道这是否可能,因为你必须在执行程序时实时编译java代码。这种方法可行吗?
另一种方法是创建一个解释器并允许用户编写伪代码,然后将其保存并转换为程序可以理解的内容。但是,如果可以完成上一个方法,那么它可能比这个方法更容易。
第一种方法可行吗?如果是这样,它比第二个好吗?或者有更简单的方法吗?
答案 0 :(得分:1)
您可以使用现有的解释器,而不是创建解释器并重新发明轮子,例如:
对于每个库,您可以选择将某些类作为变量公开给基础语言。
答案 1 :(得分:0)
Java虚拟机具有嵌入式JavaScript engine,您可以将其用于您的目的。您可以在运行时加载应用程序并执行脚本。此外,您可以轻松地注入自己的对象,实现您需要的任何功能。
答案 2 :(得分:0)
有几个选项,每个选项都有不同的努力和可能性。您可以编写自己的解释器,或使用现有的iterpreter,或使用不同的条带化语言并使用其中一个内置脚本引擎。
但是,回到初衷,让用户编写 Java代码,还有一个选项:当安装了JDK时,可以使用Java工具JDK。 (请注意,这不适用于 JRE )。
特别是,您可以使用JavaCompiler类动态编译Java类。
我明确地 NOT 想要说这是您问题的最合适的解决方案。我只是想说这是一个选项,你确实可以动态编译Java代码。
当我想要链接到一个示例(或复制并粘贴一个,带有归因)时,我注意到我在网上找到的示例plainly don't work或used temporary files,或者仅关注关于JavaCompiler
的某些特定方面及其用法(想象一下这里的博客和StackOverflow答案列表)。
我做了不找到一个MCVE,它展示了如何编译一个类的源代码(甚至更少:多个类!),并加载生成的类,完全在 - 记忆。所以我创建了这样一个实用程序类:InMemoryCompiler
类可以接收类名列表和相应的源代码,并从类名返回一个映射到Class<?>
个对象。 可以那么简单,应该就这么简单。也许有人觉得这很有用。
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.tools.FileObject;
import javax.tools.ForwardingJavaFileManager;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;
public class JavaCompilerExample
{
public static void main(String[] args)
{
String classNameA = "ExampleClassA";
String sourceA =
"public class " + classNameA + " {" + "\n" +
" public static void main(String args[]) {" + "\n" +
" System.out.println(\"Hello, compiler!\");" + "\n" +
" ExampleClassB b = new ExampleClassB();" + "\n" +
" b.someMethod();" + "\n" +
" }" + "\n" +
"}" + "\n";
String classNameB = "ExampleClassB";
String sourceB =
"public class " + classNameB + " {" + "\n" +
" public void someMethod() {" + "\n" +
" System.out.println(\"Some method was called\");" + "\n" +
" }" + "\n" +
"}" + "\n";
InMemoryCompiler c = new InMemoryCompiler();
Map<String, Class<?>> classes = c.compile(
Arrays.asList(classNameA, classNameB),
Arrays.asList(sourceA, sourceB));
try
{
Class<?> exampleClass = classes.get(classNameA);
Method m = exampleClass.getDeclaredMethod("main",
new Class[] { String[].class });
m.invoke(null, (Object) null);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
class InMemoryCompiler
{
private final Map<String, ByteArrayJavaFileObject> classFileObjects;
private final JavaCompiler javaCompiler;
private final JavaFileManager fileManager;
private final ClassLoader classLoader;
InMemoryCompiler()
{
classFileObjects =
new LinkedHashMap<String, ByteArrayJavaFileObject>();
javaCompiler = ToolProvider.getSystemJavaCompiler();
JavaFileManager standardFileManager = javaCompiler
.getStandardFileManager(null, Locale.ENGLISH, null);
fileManager = new CompiledFileManager(standardFileManager);
classLoader = new CompiledClassLoader();
}
public Map<String, Class<?>> compile(
List<String> classNames, List<String> sources)
{
List<JavaFileObject> javaFileObjects = new ArrayList<JavaFileObject>();
for (int i = 0; i < classNames.size(); i++)
{
String className = classNames.get(i);
String source = sources.get(i);
javaFileObjects.add(new StringJavaFileObject(className, source));
}
CompilationTask compilationTask = javaCompiler.getTask(
new PrintWriter(System.out), fileManager, null, null, null,
javaFileObjects);
compilationTask.call();
Map<String, Class<?>> classes = new LinkedHashMap<String, Class<?>>();
for (int i = 0; i < classNames.size(); i++)
{
String className = classNames.get(i);
try
{
Class<?> c = classLoader.loadClass(className);
classes.put(className, c);
}
catch (ClassNotFoundException e)
{
System.out.println(e);
}
}
return classes;
}
private class CompiledFileManager
extends ForwardingJavaFileManager<JavaFileManager>
{
CompiledFileManager(JavaFileManager fileManager)
{
super(fileManager);
}
@Override
public JavaFileObject getJavaFileForOutput(Location location,
String className, javax.tools.JavaFileObject.Kind kind,
FileObject sibling) throws IOException
{
ByteArrayJavaFileObject javaFileObject =
new ByteArrayJavaFileObject(className);
classFileObjects.put(className, javaFileObject);
return javaFileObject;
}
}
private class CompiledClassLoader extends ClassLoader
{
@Override
public Class<?> findClass(String name)
{
byte[] b = classFileObjects.get(name).getBytes();
return defineClass(name, b, 0, b.length);
}
}
private class ByteArrayJavaFileObject extends SimpleJavaFileObject
{
private final ByteArrayOutputStream stream;
public ByteArrayJavaFileObject(String name)
{
super(URI.create("bytes:///" + name), Kind.CLASS);
stream = new ByteArrayOutputStream();
}
@Override
public OutputStream openOutputStream() throws IOException
{
return stream;
}
public byte[] getBytes()
{
return stream.toByteArray();
}
}
private class StringJavaFileObject extends SimpleJavaFileObject
{
private final String code;
StringJavaFileObject(String name, String code)
{
super(URI.create("string:///" + name.replace('.', '/') +
Kind.SOURCE.extension), Kind.SOURCE);
this.code = code;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors)
{
return code;
}
}
}
(注意:此类中几乎没有任何错误处理。可以使用DiagnosticListener
等进行扩展,但这不是主要意图。