2009-07-23 40 views
3

Ich weiß Eval ist "böse", aber ich benutze es in einer Weise, dass der Benutzer es nie missbrauchen kann.Wie erstelle ich ein Objekt aus einer Zeichenkette in Java (wie man eine Zeichenkette auswertet)?

Sagen wir, ich habe eine Zeichenfolge "new Integer (5)". Ich möchte etwas so machen, dass ich eine Variable, sagen wir foo, auf new Integer (5) setzen kann. Etwas wie

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

Ich habe mich umgesehen und es sieht so aus, als hätte ich ein paar Optionen. Kann die Methode getSystemJavaCompiler() im ToolProvider dies tun? Oder sollte ich BeanShell verwenden? Oder gibt es noch etwas anderes? Beachten Sie, dass dies aus einer Zeichenfolge und nicht aus einer Datei stammt.

+2

Ich denke, die Angst, dass der Benutzer Eval "missbrauchen" ist nicht das Haupt Übel. In den meisten Fällen sind es Programmierer, die eval missbrauchen. In einer stark typisierten Sprache wie Java wird eval Maintenance-Programmierer nur verwirren. – Josiah

+0

Warum möchten Sie das tun? Ich kann mir keine wirkliche Situation vorstellen, in der dies nützlich wäre. Ich nehme an, Sie haben ein XY-Problem. – Raedwald

Antwort

3

würde ich eine Skriptsprache wie Beanshell, jruby, Jython, etc. verwendet

1

Die Integer class einen String in seinem Konstruktor nimmt den Wert einzustellen, den bereitgestellte Zeichenfolge unter der Annahme, nur numerischen Text enthält.

+0

Es war ein Beispiel, ich suche nach einer allgemeinen Lösung – swampsjohn

+0

Java ist eine stark typisierte Sprache, aber die meisten Sachen können String (s) für Konstruktor Parameter nehmen. Warum würden Sie eine Zeichenfolge angeben, deren Inhalt die Java-Sprache ist, anstatt Java direkt zu verwenden? –

0

Java ist eine statisch typisierte Sprache, also glaube ich nicht, dass Sie das tun können.

+0

Nun, Sie können es tun, aber Sie müssen den Java-Compiler (oder ähnliches) zur Laufzeit aufrufen und dann die resultierende Bytecode-Datei dynamisch laden. –

3

Sie müssten etwas wie Janino verwenden.

2

Diese Art der Sache ist möglich, aber es wäre schrecklich teuer für eine so einfache Aufgabe. In diesem Beispiel würde ich Class.forName() verwenden, um "Integer" einer Klasse zuzuordnen, und die Java-Reflektion ruft den Konstruktor auf.

3

Hier ist eine Möglichkeit, den größten Teil des Weges über javax.tools zu erreichen. Beachten Sie, dass dieser Code ziemlich lang ist und nicht gerade der effizienteste oder portabelste Weg, um dies zu tun, aber Sie sollten die Idee bekommen.

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

Sie können Java Scripting API verwenden. Standardsprache ist JavaScript, aber Sie können jede Sprache anschließen. es würde jedoch Java 1.6 erfordern.

Verwandte Themen