在运行时

时间:2016-07-06 07:32:23

标签: java reflection runtime-compilation ecj

在运行时,在我的Java程序中,给定一个String,我想知道返回类型。例如:

  • 1 + 1返回int
  • 1L + 1L返回long
  • 1L + 1返回long
  • 1 + 1.5返回double
  • 1 + 2 - 3 * 4 / 5返回int
  • 1 / 0返回int
  • 1 + Math.nextInt()返回int
  • 1.5 + Math.nextInt()返回double
  • Color.RED返回java.awt.Color
  • 鉴于a是一个int:a + 1返回int
  • 鉴于a是一个int:a + 1.5返回double

无需实际评估代码:我只需要返回类型。 如何使用JDK运行时编译器,ECJ JDT或任何其他纯Java依赖项执行此操作?

详细代码:这是针对此代码的简化伪代码单元测试:

public static void ExpressionTyper {
    public String determineType(String expression, Map<String, String> variableTypes) {
       ... // How do I implement this?
    }
}
public void ExpressionTyperTest {
    @Test public void determineType() {
        assertEquals("int", ExpressionTyper.determineType("1 + 1", emptyMap());
        assertEquals("long", ExpressionTyper.determineType("1 + 1L", emptyMap());
        assertEquals("double", ExpressionTyper.determineType("1 + 1.5", emptyMap());
        assertEquals("int", ExpressionTyper.determineType("a + 1", mapOf({"a", "int"}));
        assertEquals("int", ExpressionTyper.determineType("a + b", mapOf({"a", "int"}, {"b", "int"}));
        assertEquals("double", ExpressionTyper.determineType("a + b", mapOf({"a", "double"}, {"b", "int"}));
    }
}

3 个答案:

答案 0 :(得分:5)

我认为取决于您希望能够处理的输入范围。

你看,最后你要问:我如何在运行时评估字符串表达式。

所以,简短的回答是:你需要某种解释器/ REPL实现;或至少“部分”。

另一种方法可能是使用javax编译器简单地编译事物,然后推导出类型,如here

其他选项与某些“编译器构造”主题相似,如constant folding

答案 1 :(得分:3)

我没试过这个......

  1. 将表达式包装在如下代码中:

      public class Test {
          private Test xxx = <<insert expression>>;
      }
    
  2. 编译代码。

  3. 解析编译错误消息以提取编译器对RHS类型的想法。
  4. 问题是像Math.nextInt()这样的表达式可能需要导入才能编译,我怀疑是否有一种防弹方式来推断导入应该是什么。不过,这应该适用于一个有用的表达式子集。

    这种方法也很脆弱且不可移植,因为它取决于编译错误消息的精确形式,这可能与编译器/版本有关。

    更好的解决方案(但更多工作)是为Java子集表达式语言实现解析器和类型检查器。

答案 2 :(得分:1)

要为任意表达式执行此操作,您需要一个完整的Java前端,它将解析字符串并确定其类型。从本质上讲,您需要编译器所做的事情。

Eclipse JDT可能会提供解决方案;我不太熟悉它。

我们的DMS软件再造工具包与DMS的完整Java前端可以做到这一点。 DMS根据它正在使用的前端解析源,然后可以调用前端服务来分析该代码。

Java前端以各种版本和各种解析方式(文件,流,字符串)为Java提供解析,然后为解析后的文本构建树。可以要求内置于前端的Java名称解析器根据代码中特定点有效的作用域规则计算任意表达式的类型。

对于OP,这可能不是他想要的,因为他坚持基于Java的答案而DMS不是基于Java的(他确实要求“......任何其他依赖”)。最接近的可能是调用DMS作为子进程,并要求DMS打印表达式的类型。如果他的表达式具有极少数类型,如他的示例所示,这可能会起作用。如果没有,他将需要一个复杂的机制来读取表达式类型,这些表达式的完整荣耀(例如,包解析,模板化形式)可能相当复杂。

如果OP愿意严格限制他只需简单算术处理的表达式类,他就可以构建自己的表达式解析器并为自己计算出类型。请参阅我的SO答案,了解如何手动构建递归下降解析器:https://stackoverflow.com/a/2336769/120163同样的答案线程还展示了如何构建树并“评估”它;他可以根据类型而不是计算结果来评估它。

否则他将需要大锤子。

相关问题