2009-07-23 2 views
3

Я знаю, что eval является «злым», но я использую его так, чтобы пользователь не мог его оскорблять.Как создать объект из строки в Java (как оценить строку)?

Предположим, у меня есть строка «new Integer (5)». Я хочу сделать что-то такое, что я могу установить переменную, скажем, foo, на новый Integer (5). Что-то вроде

Integer foo; 
String bar = "new Integer(5)" 
*magic happens* 
System.out.println(foo) -> 5 

Я осмотрелся и, похоже, у меня есть несколько вариантов. Может ли метод getSystemJavaCompiler() в ToolProvider сделать это? Или я должен использовать BeanShell? Или есть что-то еще? Обратите внимание, что это строка, а не файл.

+2

Я думаю, что страх, что пользователь будет «злоупотреблять» eval, не является главным злом. В большинстве случаев программисты злоупотребляют eval. На строго типизированном языке, таком как Java, eval будет путать программистов обслуживания. – Josiah

+0

Почему вы хотите это сделать. Я не думаю о реальной ситуации, в которой это было бы полезно. Я думаю, у вас есть проблема XY. – Raedwald

ответ

3

Я хотел бы использовать язык сценариев, как Beanshell, JRuby, Jython и т.д.

1

Integer class принимает строку в своем конструкторе, чтобы установить значение, предполагая, предоставленную строку содержит только числовой текст.

Integer foo; 

public void setFoo(String str) { 
    if(isInt(str)) { 
    foo = new Integer(str.trim()); 
    } 
} 

// Returns a boolean based on if the provided string contains only numbers 
private boolean isInt(String str) { 
    boolean isInt = true; 

    try { 
    Integer.parseInt(str.trim()); 
    } catch (NumberFormatException nfe) { 
    isInt = false; 
    } 

    return isInt; 
} 

// Get the value as an int rather than Integer 
public int getIntValue(String str) { 
    return Integer.parseInt(str.trim()); 
} 
+0

Это был пример, я ищу общее решение – swampsjohn

+0

Java - это строго типизированный язык, но большинство вещей может принимать String (s) для параметров конструктора. Почему вы поставляете строку, содержимое которой является языком Java, а не напрямую использовать Java? –

0

Java - это статически типизированный язык, поэтому я не думаю, что вы можете это сделать.

+0

Ну, вы можете это сделать, но включает вызов компилятора Java (или аналогичного) во время выполнения, а затем динамическую загрузку полученного файла байт-кода. –

3

Вам нужно будет использовать что-то вроде Janino.

2

Это возможно, но это было бы ужасно дорого для такой простой задачи. В этом примере я бы предпочел использовать Class.forName() для сопоставления «Integer» с классом, а отражение Java вызывает конструктор.

3

Вот возможный способ получить большую часть пути с помощью javax.tools. Обратите внимание, что этот код довольно длинный, а не самый эффективный или переносимый способ сделать это, но вы должны получить эту идею.

import javax.tools.*; 
import java.util.*; 
import java.io.*; 
import java.lang.reflect.*; 

public class Test { 
    public static void main(String[] args) throws Exception { 
    JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); 
    JavaFileObject fileObj = 
     new StringJavaFileObject("public class InterpTest { public static void test() { System.out.println(\"Hello World\"); } }"); 
    List<JavaFileObject> tasks = new ArrayList<JavaFileObject>(); 
    tasks.add(fileObj); 

    JavaFileManager defFileMgr = compiler.getStandardFileManager(null, null, null); 
    MemoryJavaFileManager fileMgr = new MemoryJavaFileManager(defFileMgr); 
    compiler.getTask(null, fileMgr, null, null, null, tasks).call(); 

    ClassLoader loader = new ByteArrayClassLoader(); 
    Class clazz = loader.loadClass("InterpTest"); 
    Method method = clazz.getMethod("test"); 
    method.invoke(null); 
    } 

    public static class StringJavaFileObject extends SimpleJavaFileObject { 
    protected String str; 

    public StringJavaFileObject(String str) { 
     super(java.net.URI.create("file:///InterpTest.java"), JavaFileObject.Kind.SOURCE); 
     this.str = str; 
    } 

    @Override 
    public CharSequence getCharContent(boolean ignoreEncErrors) { 
     return str; 
    } 
    } 

    public static class MemoryJavaFileObject extends SimpleJavaFileObject { 
    public static ByteArrayOutputStream out = new ByteArrayOutputStream(); 

    public MemoryJavaFileObject(String uri, JavaFileObject.Kind kind) { 
     super(java.net.URI.create(uri), kind); 
    } 

    @Override 
    public OutputStream openOutputStream() { 
     return out; 
    } 
    } 

    public static class ByteArrayClassLoader extends ClassLoader { 
    public Class findClass(String name) { 
     byte[] bytes = MemoryJavaFileObject.out.toByteArray(); 
     return super.defineClass(name, bytes, 0, bytes.length); 
    } 
    } 

    public static class MemoryJavaFileManager implements JavaFileManager { 
    protected JavaFileManager parent; 

    public JavaFileObject getJavaFileForOutput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException { 
     return new MemoryJavaFileObject("file:///InterpTest.class", kind); 
    } 

    public MemoryJavaFileManager(JavaFileManager parent) { this.parent = parent; } 
    public void close() throws IOException { parent.close(); } 
    public void flush() throws IOException { parent.flush(); } 
    public ClassLoader getClassLoader(JavaFileManager.Location location) { return parent.getClassLoader(location); } 
    public FileObject getFileForInput(JavaFileManager.Location location, String packageName, String relName) throws IOException { return parent.getFileForInput(location, packageName, relName); } 
    public FileObject getFileForOutput(JavaFileManager.Location location, String packageName, String relName, FileObject sibling) throws IOException { return parent.getFileForOutput(location, packageName, relName, sibling); } 
    public JavaFileObject getJavaFileForInput(JavaFileManager.Location location, String className, JavaFileObject.Kind kind) throws IOException { return parent.getJavaFileForInput(location, className, kind); } 
    public boolean handleOption(String current, Iterator<String> remaining) { return parent.handleOption(current, remaining); } 
    public boolean hasLocation(JavaFileManager.Location location) { return parent.hasLocation(location); } 
    public String inferBinaryName(JavaFileManager.Location location, JavaFileObject file) { return parent.inferBinaryName(location, file); } 
    public boolean isSameFile(FileObject a, FileObject b) { return parent.isSameFile(a, b); } 
    public Iterable<JavaFileObject> list(JavaFileManager.Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException { return parent.list(location, packageName, kinds, recurse); } 
    public int isSupportedOption(String option) { return parent.isSupportedOption(option); } 
    } 
} 
0

Вы можете использовать Java Scripting API. Язык по умолчанию - это JavaScript, но вы можете подключить любой язык. для этого потребуется java 1.6.

Смежные вопросы