7

我希望在 Java 中创建一些将在编译时调用的函数 - 并完全从运行时排除(即编译的类文件)。这些函数可以写入对于软件的不同版本可能具有不同值的变量。这样做的野兽方法是什么?这些可能是“最终”变量。

顺便说一下,这是一个 GWT 项目,函数应该能够写入客户端和服务器端。

考虑这个简单的例子:

final int x = 10;
final int y = x/100;
final int z = y + x - 500;

这里定义了三个“最终”变量 x、y 和 z,其中 y 和 z 的值取决于常数 x!这意味着 y 和 z 的值在程序中也永远不会真正改变。但是因为它们必须被定义为 x 的函数——它们的值将在运行时计算——这在运行时是不必要的资源使用。编译时函数将硬编码 y 和 z 的值并稍微加快运行时间!显然,这是一个简单的示例,但我的实际用例需要完成大量复杂且耗时的计算,这会减慢我的应用程序的速度。如果您快速需要具有不同变量值的应用程序的多个版本,那么在编译时计算它会非常好,并且可以避免由于手动硬编码数字而导致的错误。

此外,请考虑以下示例:

final String value = getMyValue("argument");

final String getMyValue(String variable){
    return variable + "Lol!";
}

您是否希望在编译时计算“值”的值?在客户端和服务器端?

4

2 回答 2

3

对于 GWT 客户端代码,您可以使用延迟绑定在编译时生成 Java 代码。您创建一个com.google.gwt.core.ext.Generator. Google 使用延迟绑定进行 ie 本地化等。我用它为 GWT 实现了一个简单的 XML 映射器,但这是封闭源代码。

例如,您创建一个接口com.foo.Constants

public interface Constants {
  int getY();
  int getZ();
}

并为您的常量创建一个生成器:

public class ConstantsGenerator extends Generator {
  private static final String PACKAGE_NAME = "com.foo";
  private static final String SIMPLE_CLASS_NAME = "GeneratedConstants";
  private static final String CLASS_NAME = PACKAGE_NAME + "." + SIMPLE_CLASS_NAME;

  @Override
  public String generate(TreeLogger logger, GeneratorContext context,
    String typeName) throws UnableToCompleteException {
    // Get a SourceWriter to create the class GeneratedConstants.
    // If it returns null, then this class was already created during this compilation
    // step and we only return the name of class.
    final SourceWriter sourceWriter = getSourceWriter(logger, context);
    if(sourceWriter != null) {
      // Otherwise create the new class ...
      generateConstantsClass(sourceWriter, logger);
    }

    return CLASS_NAME;
  }

  private SourceWriter getSourceWriter(TreeLogger logger, GeneratorContext context) {
    final ClassSourceFileComposerFactory composer = 
      new ClassSourceFileComposerFactory(PACKAGE_NAME, SIMPLE_CLASS_NAME);

    // The generated class implements com.foo.Constants
    composer.addImplementedInterface("com.foo.Constants");

    // Add some imports, if needed
    // composer.addImport("java.util.Map");

    final PrintWriter printWriter = context.tryCreate(logger, PACKAGE_NAME, SIMPLE_CLASS_NAME);
    if(printWriter == null)
      return null;
    else {
      final SourceWriter sw = composer.createSourceWriter(context, printWriter);
      return sw;
    }
  }

  private void generateConstantsClass(SourceWriter sourceWriter, TreeLogger logger) {
    final int y = calculateY();  // Do here the heavy calculation for y.
    final int z = calculateZ();  // Do here the heavy calculation for z.

    // Create the source code for the two Methods getY() and getZ().
    sourceWriter.println(String.format("public int getY() { return %d; }", y));
    sourceWriter.println(String.format("public int getZ() { return %d; }", z));

    sourceWriter.commit(logger);
  }
}

然后你必须配置你的 GWT 模块 XML 文件:

<generate-with class="com.foo.ConstantsGenerator">
  <when-type-assignable class="com.foo.Constants" />
</generate-with>

然后在您的 GWT 客户端代码中,您可以像这样创建生成的类的实例:

public Constants getConstants() {
  return (Constants) GWT.create(Constants.class);
}

但这仅适用于客户端代码。只预先计算一些值可能有点太多的开销。

在构建脚本中包含代码生成步骤会更容易。然后,您可以在服务器和客户端代码中使用您生成的类。

但是对于预先计算的值,这仍然可能是太多的开销。如果您只使用预先计算的值生成一个简单的文件(如属性文件)并在运行时加载它,则可以完全避免生成代码。

在服务器端,这应该没问题。对于客户端,您可以使用 JSP 生成 GWT 主机页面,并将预先计算的值包含为 JavaScript 字典。在 GWT 代码中,很容易访问这些值。

于 2013-05-28T16:30:51.473 回答
2

您正在寻找编译时元编程。这意味着在编译阶段,您的源代码或抽象语法树会以某种方式进行预处理或修改。

如果您查看 C++,就会发现模板元编程

至于Java,我不知道实现这一点的直接方法。

所以下一个表亲是Scala:他们称之为,它允许您在编译时修改抽象语法树。例如,我使用它来完全删除代码,基于布尔条件。

鉴于兼容性,您可以在 Scala 中编写复杂的计算,并在 Java 中使用它。

于 2013-05-28T14:50:28.403 回答