Is static metaprogramming possible in Java?
Asked Answered
G

10

40

I am a fan of static metaprogramming in C++. I know Java now has generics. Does this mean that static metaprogramming (i.e., compile-time program execution) is possible in Java? If so, can anyone recommend any good resources where one can learn more about it?

Gorton answered 21/9, 2008 at 22:10 Comment(3)
Out of curiosity: Beyond the technical challenge, what are the practical benefits of static metaprogramming?Pentahedron
@Uri: Moving as much work and error sources to compile-time as possible - code-generation, optimization and static polymorphism would be some keywords.Rosemarie
If you can reify/typify properties, you can create expressions and structures that share those properties, and it will compile only if it is/behaves how you statically assert it is/does. So, you can produce a program whose existence proves its correctness. That's just the start.Gensmer
K
13

The short answer

This question is nearly more than 10 years old, but I am still missing one answer to this. And this is: yes, but not because of generics and note quite the same as C++.

As of Java 6, we have the pluggable annotation processing api. Static metaprogramming is (as you already stated in your question)

compile-time program execution

If you know about metaprogramming, then you also know that this is not really true, but for the sake of simplicity, we will use this. Please look here if you want to learn more about metaprogramming in general.

The pluggable annotation processing api is called by the compiler, right after the .java files are read but before the compiler writes the byte-code to the .class files. (I had one source for this, but i cannot find it anymore.. maybe someone can help me out here?).

It allows you, to do logic at compile time with pure java-code. However, the world you are coding in is quite different. Not specifically bad or anything, just different. The classes you are analyzing do not yet exist and you are working on meta data of the classes. But the compiler is run in a JVM, which means you can also create classes and program normally. But furthermore, you can analyze generics, because our annotation processor is called before type erasure.

The main gist about static metaprogramming in java is, that you provide meta-data (in form of annotations) and the processor will be able to find all annotated classes to process them. On (more easy) example can be found on Baeldung, where an easy example is formed. In my opinion, this is quite a good source for getting started. If you understand this, try to google yourself. There are multiple good sources out there, to much to list here. Also take a look at Google AutoService, which utilizes an annotation processor, to take away your hassle of creating and maintaining the service files. If you want to create classes, i recommend looking at JavaPoet.

Sadly though, this API does not allow us, to manipulate source code. But if you really want to, you should take a look at Project Lombok. They do it, but it is not supported.


Why is this important (Further reading for the interested ones among you)

TL;DR: It is quite baffling to me, why we don't use static metaprogramming as much as dynamic, because it has many many advantages.

Most developers see "Dynamic and Static" and immediately jump to the conclusion that dynamic is better. Nothing wrong with that, static has a lot of negative connotations for developers. But in this case (and specifically for java) this is the exact other way around.

Dynamic metaprogramming requires reflections, which has some major drawbacks. There are quite a lot of them. In short: Performance, Security, and Design.

Static metaprogramming (i.e. Annotation Processing) allows us to intersect the compiler, which already does most of the things we try to accomplish with reflections. We can also create classes in this process, which are again passed to the annotation processors. You then can (for example) generate classes, which do what normally had to be done using reflections. Further more, we can implement a "fail fast" system, because we can inform the compiler about errors, warnings and such.

To conclude and compare as much as possible: let us imagine Spring. Spring tries to find all Component annotated classes at runtime (which we could simplify by using service files at compile time), then generates certain proxy classes (which we already could have done at compile time) and resolves bean dependencies (which, again, we already could have done at compile time). Jake Whartons talk about Dagger2, in which he explains why they switched to static metaprogramming. I still don't understand why the big players like Spring don't use it.

This post is to short to fully explain those differences and why static would be more powerful. If you want, i am currently working on a presentation for this. If you are interested and speak German (sorry about that), you can have a look at my website. There you find a presentation, which tries to explain the differences in 45 minutes. Only the slides though.

Kinswoman answered 24/8, 2019 at 12:38 Comment(0)
H
24

No, this is not possible. Generics are not as powerful as templates. For instance, a template argument can be a user-defined type, a primitive type, or a value; but a generic template argument can only be Object or a subtype thereof.

Edit: This is an old answer; since 2011 we have Java 7, which has Annotations that can be used for such trickery.

Hound answered 21/9, 2008 at 22:13 Comment(3)
Yet reflection is more powerful than templates--why would you use the wrong tool (generics) for the job?Sward
One word: type safety. (Zero... one... see? One word.)Hound
@kravemir: Code generation or other external tools don't really count. Annotations do, but didn't exist at the time I wrote this :) Edited.Hound
B
13

Take a look at Clojure. It's a LISP with Macros (meta-programming) that runs on the JVM and is very interoperable with Java.

Bargainbasement answered 22/9, 2008 at 18:39 Comment(0)
K
13

The short answer

This question is nearly more than 10 years old, but I am still missing one answer to this. And this is: yes, but not because of generics and note quite the same as C++.

As of Java 6, we have the pluggable annotation processing api. Static metaprogramming is (as you already stated in your question)

compile-time program execution

If you know about metaprogramming, then you also know that this is not really true, but for the sake of simplicity, we will use this. Please look here if you want to learn more about metaprogramming in general.

The pluggable annotation processing api is called by the compiler, right after the .java files are read but before the compiler writes the byte-code to the .class files. (I had one source for this, but i cannot find it anymore.. maybe someone can help me out here?).

It allows you, to do logic at compile time with pure java-code. However, the world you are coding in is quite different. Not specifically bad or anything, just different. The classes you are analyzing do not yet exist and you are working on meta data of the classes. But the compiler is run in a JVM, which means you can also create classes and program normally. But furthermore, you can analyze generics, because our annotation processor is called before type erasure.

The main gist about static metaprogramming in java is, that you provide meta-data (in form of annotations) and the processor will be able to find all annotated classes to process them. On (more easy) example can be found on Baeldung, where an easy example is formed. In my opinion, this is quite a good source for getting started. If you understand this, try to google yourself. There are multiple good sources out there, to much to list here. Also take a look at Google AutoService, which utilizes an annotation processor, to take away your hassle of creating and maintaining the service files. If you want to create classes, i recommend looking at JavaPoet.

Sadly though, this API does not allow us, to manipulate source code. But if you really want to, you should take a look at Project Lombok. They do it, but it is not supported.


Why is this important (Further reading for the interested ones among you)

TL;DR: It is quite baffling to me, why we don't use static metaprogramming as much as dynamic, because it has many many advantages.

Most developers see "Dynamic and Static" and immediately jump to the conclusion that dynamic is better. Nothing wrong with that, static has a lot of negative connotations for developers. But in this case (and specifically for java) this is the exact other way around.

Dynamic metaprogramming requires reflections, which has some major drawbacks. There are quite a lot of them. In short: Performance, Security, and Design.

Static metaprogramming (i.e. Annotation Processing) allows us to intersect the compiler, which already does most of the things we try to accomplish with reflections. We can also create classes in this process, which are again passed to the annotation processors. You then can (for example) generate classes, which do what normally had to be done using reflections. Further more, we can implement a "fail fast" system, because we can inform the compiler about errors, warnings and such.

To conclude and compare as much as possible: let us imagine Spring. Spring tries to find all Component annotated classes at runtime (which we could simplify by using service files at compile time), then generates certain proxy classes (which we already could have done at compile time) and resolves bean dependencies (which, again, we already could have done at compile time). Jake Whartons talk about Dagger2, in which he explains why they switched to static metaprogramming. I still don't understand why the big players like Spring don't use it.

This post is to short to fully explain those differences and why static would be more powerful. If you want, i am currently working on a presentation for this. If you are interested and speak German (sorry about that), you can have a look at my website. There you find a presentation, which tries to explain the differences in 45 minutes. Only the slides though.

Kinswoman answered 24/8, 2019 at 12:38 Comment(0)
F
8

What do you exactly mean by "static metaprogramming"? Yes, C++ template metaprogramming is impossible in Java, but it offers other methods, much more powerful than those from C++:

  • reflection
  • aspect-oriented programming (@AspectJ)
  • bytecode manipulation (Javassist, ObjectWeb ASM, Java agents)
  • code generation (Annotation Processing Tool, template engines like Velocity)
  • Abstract Syntax Tree manipulations (APIs provided by popular IDEs)
  • possibility to run Java compiler and use compiled code even at runtime

There's no best method: each of those methods has its strengths and weaknesses. Due to flexibility of JVM, all of those methods in Java can be used both at compilation time and runtime.

Form answered 9/12, 2010 at 14:54 Comment(4)
Nope, that's not "more powerful". Every single one of these mechanisms is either possible in C++ as well, but usually it is worked around by some C++ templating library so the tools look less powerful but in reality they aren't. (I see a lot of syntactic and semantic issues with C++'s templates, but lack of power is definitely not one of them.)Victorie
Everything is possible in any Turing-complete language, but we also speak about ease of use here. For example if you're writing a (let's say) serialization or dependency injection or mocking library: in Java the solution is very simple: just use reflection and iterate through class's fields. Even no "data" class modifications are required. In C++ you have to modify the "data" class and use some preprocessor macros, or use use some code generator. Not so simple and practical as Java's way.Form
C++ templates are strictly more powerful than Java generics specifically if you factor in ease of use (if you don't, power comparisons stop making sense anyway). You can do primitives, you can do compile-time calculations, you can add specializations for specific instantiations; none of this is possible with Java generics. (Note that I don't claim that more power is always better, that's a different discussion.)Victorie
Now for the other tools and mechanisms: You have an apples-vs-oranges comparison there. Similar tooling exists for C++ - not bytecode manipulation, obviously, but AOP does exist (through templates I believe), code generation is firmly template land so that's actually covered, AST manipulation doesn't count because it's not Java but IDE (and obviously would exist just as well for C++), and running a full compiler - well, you use Lua in C++ land, and you wouldn't want a full Java compiler on the customer's machine anyway (usually).Victorie
L
4

No. Even more, generic types are erased to their upper bound by the compiler, so you cannot create a new instance of a generic type T at runtime.

The best way to do metaprogamming in Java is to circumvent the type erasure and hand in the Class<T> object of your type T. Still, this is only a hack.

Libertinage answered 21/9, 2008 at 22:18 Comment(0)
I
2

If you need powerful compile-time logic for Java, one way to do that is with some kind of code generation. Since, as other posters have pointed out, the Java language doesn't provide any features suitable for doing compile-time logic, this may be your best option (iff you really do have a need for compile-time logic). Once you have exhausted the other possibilities and you are sure you want to do code-generation, you might be interested in my open source project Rjava, available at:

http://www.github.com/blak3mill3r

It is a Java code generation library written in Ruby, which I wrote in order to generate Google Web Toolkit interfaces for Ruby on Rails applications automatically. It has proved quite handy for that.

As a warning, it can be very difficult to debug Rjava code, Rjava doesn't do much checking, it just assumes you know what you're doing. That's pretty much the state of static metaprogramming anyway. I'd say it's significantly easier to debug than anything non-trivial done with C++ TMP, and it is possible to use it for the same kinds of things.

Anyway, if you were considering writing a program which outputs Java source code, stop right now and check out Rjava. It might not do what you want yet, but it's MIT licensed, so feel free to improve it, deep fry it, or sell it to your grandma. I'd be glad to have other devs who are experienced with generic programming to comment on the design.

Inexorable answered 16/9, 2010 at 5:20 Comment(0)
E
2

Lombok offers a weak form of compile time metaprogramming. However, the technique they use is completely general.

See Java code transform at compile time for a related discussion

Ellaelladine answered 12/8, 2013 at 11:58 Comment(0)
I
2

You can use a metaprogramming library for Java such as Spoon: https://github.com/INRIA/spoon/

Instantaneous answered 17/11, 2020 at 6:58 Comment(0)
K
1

No, generics in Java is purely a way to avoid casting of Object.

Kiehl answered 21/9, 2008 at 22:12 Comment(1)
Not technically true, since some generics are available at runtime. e.g, Gafter's Gadget: gafter.blogspot.ca/2006/12/super-type-tokens.htmlEllaelladine
D
1

In a very reduced sense, maybe? http://michid.wordpress.com/2008/08/13/type-safe-builder-pattern-in-java/

Desexualize answered 23/9, 2008 at 12:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.