【问题标题】:How to create an object from a string in Java (how to eval a string)?如何从 Java 中的字符串创建对象(如何评估字符串)?
【发布时间】:2009-07-23 00:11:41
【问题描述】:

我知道 eval 是“邪恶的”,但我使用它的方式是用户永远不能滥用它。

假设我有一个字符串“new Integer(5)”。我想做一些事情,这样我就可以将一个变量(比如说 foo)设置为 new Integer(5)。类似的东西

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

我环顾四周,似乎有几个选择。 ToolProvider 中的 getSystemJavaCompiler() 方法可以做到这一点吗?还是我应该使用 BeanShell?或者还有别的什么?请注意,这是来自字符串,而不是文件。

【问题讨论】:

  • 我认为担心用户会“滥用”评估并不是主要的邪恶。在大多数情况下,滥用 eval 的是程序员。在 Java 这样的强类型语言中,eval 只会让维护程序员感到困惑。
  • 你为什么要这样做。我想不出这将是有用的实际情况。我猜你有一个 XY 问题。

标签: java eval


【解决方案1】:

我会使用 beanshell、jruby、jython 等脚本语言。

【讨论】:

  • 如果你想对java源代码生成进行单元测试,你不能使用脚本语言。
【解决方案2】:

你必须使用Janino之类的东西。

【讨论】:

    【解决方案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); }
      }
    }
    

    【讨论】:

      【解决方案4】:

      这种事情是有可能的,但对于这样简单的任务来说,这将是非常昂贵的。在这个例子中,我会考虑使用 Class.forName() 将“Integer”映射到一个类,然后 Java 反射调用构造函数。

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 这是一个例子,我正在寻找一个通用的解决方案
        • Java 是一种强类型语言,但大多数东西都可以使用 String(s) 作为构造函数参数。为什么要提供内容为 Java 语言的字符串而不是直接使用 Java?
        【解决方案6】:

        Java 是一种静态类型语言,所以我认为你做不到。

        【讨论】:

        • 你可以做到,但需要在运行时调用 Java 编译器(或类似的),然后动态加载生成的字节码文件。
        【解决方案7】:

        您可以使用Java Scripting API。默认语言是 JavaScript,但您可以插入任何语言。不过它需要 java 1.6。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-17
          • 1970-01-01
          • 1970-01-01
          • 2019-04-22
          • 1970-01-01
          • 1970-01-01
          • 2013-10-29
          • 1970-01-01
          相关资源
          最近更新 更多