我希望在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!";
}
您是否期望在编译时计算“值”的值?在客户端和服务器端?
答案 0 :(得分:3)
对于GWT客户端代码,您可以使用deferred binding在编译时生成Java代码。您创建了com.google.gwt.core.ext.Generator
的子类。
Google使用延迟绑定进行本地化等。我用它来为GWT实现一个简单的XML映射器,但这是封闭的源代码。
例如,您创建了一个界面com.foo.Constants
:
public interface Constants {
int getY();
int getZ();
}
为常量创建一个Generator:
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 dicationary包含在内。在GWT代码中,可以轻松访问这些值。
答案 1 :(得分:2)
您正在寻找编译时元编程。这意味着在编译阶段,您的源或抽象语法树会以某种方式进行预处理或修改。
如果你看一下C ++,就会有模板元编程。
至于Java,我并不知道有直接的方法来实现这一点。
所以下一个堂兄会是 Scala :他们称之为宏,它允许你在编译时修改抽象语法树 。例如,我使用它来完全删除代码,基于布尔条件。
鉴于兼容性,您可以在Scala中编写复杂的计算,并从Java中使用它。