Java static metaprogramming

↘锁芯ラ 提交于 2019-12-05 09:40:13

8 Years and not yet answered. Because of that, i will try to answer it, to your satisfaction.

I fill furthermore concentrate on the static part of the question.

TL;DR:

You will not find copy and paste code in this answer.

Is it feasible?

Yes, absolutely.

How can I find out actual annotation class that is referenced by this identifier?

You will have to use the RoundEnvironment within an Annotation Processor to get the TypeElement.

Static Metaprogramming

Static metaprogramming (which you asked for) is metaprogramming done at compile time. By Kontrast: Dynamic metaprogramming is metaprogramming done at run time. And metaprogramming it self is the design of programs, that handle other programs as data.

Pfeh, a lot to take in. If you are interested in this topic, a more or less good source for that is wikipedia.

Your target would be, to generate a class at compile time. For run time, this would be done with something like cglib. But, since you choose static (and for all the right reasons), i will not explain this.

The concept you are looking for is the annotation processor. The link is a link to Baeldung, where they do exactly, what you are looking for, only with the builder pattern in mind. You will love to hear, that this scenario is highly encouraged and easy to do with the annotation processor API. It even allows you, to generate code, which again is passed to the same or another annotation processor, without you doing anything.

Before jumping right in, try to google yourself about "Java Annotation Processing". There are a lot of good sources out there, which will help you. To much, to list here. Just note, that coding in an annotation processor is different than coding normally. Not a huge difference, but the classes you are working on are not yet created. So keep this in mind and don't get discouraged!

Using the Annotation Processor

Your basic annotation processor would look something like this:

@SupportedAnnotationTypes("package.of.MyAnnotation")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@AutoService(Processor.class)
public class BuilderProcessor extends AbstractProcessor {

    @Override
    public boolean process(Set<? extends TypeElement> annotations, 
                           RoundEnvironment roundEnv) {
        // First let's find all annotated elements
        Set<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(MyAnnotation.class);
        // Handle all the annotated classes
        return false;
    }
}

The AutoService Annotation is used, to dynamically register your annotation processor. It comes from an external source, just so you don't wonder, why this code won't compile.

In the handle all annotated classes part, you have the annotated Elements (which are the annotated classes). You now would have to verify, that they are classes and not interfaces or other annotations. This is because @Target(ElementType.Type) aims at any type, which includes interfaces and annotations. Furthermore, you would want to verify, that anything you require is present, or print an error to the compiler using the Messager.

If you print an error here (for example), you will stop compiling and the error will be seen in most modern IDEs. It can be reached by calling roundEnv.getMessager()

Afterwards you can generate a new class and write it to the input of the compiler, as a .java file. This can be done by using the Filer.

An answer in StackOverflow really does no justice to this topic. I highly recommend looking at the Baeldung example and trying to uncover things from there. This API is as old as Java 6, but still not that greatly used. I encourage you, the reader, to try it out for yourself :)

Yes, it is possible and I know at least 2 ways.

First, "traditional" way is to write ant task/maven plugin/just command line java utility that scans given file path and calls for each class something like Class.forName(className).getAnnotations(MyAnnotation.class). If this is not null discover class using reflection and do what you need.

Other way is a little bit more difficult but more powerful. You can implement your own Processor (that implements javax.annotation.processing.Processor or even better extends javax.annotation.processing.AbstractProcessor. Your processor will just have to be placed to the compiler classpath and it will run automatically when compiler runs. You can even configure your IDE (e.g. Eclipse) to run your processor. It is a kind of extension to java compiler. So, every time eclipse builds your project it runs the processor and creates all new classes according to new annotations you have added.

Please take a look on this project as a reference.

take a look at https://github.com/rzwitserloot/lombok/, It add methods as you described. such as

  • @Getter add getter methods based on fields
  • @Setter
  • @ToString add toString() methods base on the fields
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!