Is it possible to create any compile-time functions in Java?

I want to create some functions in Java that will be called at compile time, and be completely excluded from the runtime (i.e. compiled class files). These functions can be written to variables, which can have different values ​​for different software versions. What kind of beast? These can be "final" variables.

This, by the way, is a GWT project, and functions should be able to write both on the client side and on the server side.

Consider this simple example:

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

Here are three “final” variables x, y, and z, where the values ​​of y and z depend on x, which is a constant! This means that the values ​​of y and z also never change in the program. But since they must be defined as a function of x, their values ​​will be computed at runtime, which is an unnecessary use of resources at runtime. The compile-time function will rigidly set the y and z values ​​and speed up the execution time a bit! Obviously, this is a simple example, but my real use case has many complex time consuming things to do, which slows down my application. Calculating this at compile time would be really good and would avoid errors caused by hard-coding numbers manually if you quickly needed several releases of applications with different variable values.

Also, consider this example:

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

Was the calculation of the value "value" expected at compile time? Both on the client side and on the server side?

+4
source share
2 answers

For GWT client-side code, you can use delayed binding to generate Java code at compile time. You create a subclass of com.google.gwt.core.ext.Generator . Google uses lazy binding, i.e. localization, etc. I used it to implement a simple XML converter for GWT, but it is a closed source.

For example, you create the com.foo.Constants interface:

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

And create a generator for your constants:

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

Then you need to configure the GWT module XML file:

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

Then, in your GWT client code, you can instantiate your generated class as follows:

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

But this only works for client-side code. And maybe a little too much overhead to just pre-calculate some values.

It would be easier to include the code generation step in your build scripts. Then you can use the created classes both on the server and on the client code.

But for predefined values, this can still be too large. You may not generate code at all if you generate a simple file (for example, a properties file) with your pre-calculated values ​​and load it at run time.

On the server side, this should not be a problem. For the client side, you can generate your GWT homepage using JSP and include your precalculated values ​​as a JavaScript dicationary. In the GWT code, these values ​​are easy to access.

+3
source

You are looking for metaprogramming compile time. This means that at the compilation stage, the original or abstract syntax tree is pre-processed or modified in a certain way.

If you look at C ++, there is meta-programming of templates.

As for Java, I don't know about direct means to achieve this.

So, the next cousin will be Scala: they call it macros, and it allows you to modify the abstract syntax tree at compile time. For example, I use this to completely remove code based on logical conditions.

Given compatibility, you can write complex calculations in Scala and use it with Java.

+2
source

Source: https://habr.com/ru/post/1483169/


All Articles