Java静态元编程 [英] Java static metaprogramming

查看:29
本文介绍了Java静态元编程的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想实现一个注解处理器,它会根据现有的原型"类生成新的类.

I'd like to implement annotation processor that will generate new class based on existing "prototype" class.

import java.util.List

@MyAnnotation
class MySuperClassPrototype {
    static MySuperClassPrototype createInstance() {
      return new MySuperClassPrototype();
    }
}

由于下面的代码.将生成如下新的源文件(编译单元):

As a result of code below. The following new source file (compilation unit) will be generated:

import java.util.List

class MySuperClass {
    static MySuperClass createInstance() {
      return new MySuperClass();
    }
    public void specialAddedMethod() {
      /*...*/
    }
}

我想复制所有顶级导入语句和静态成员,而不是原型类的静态成员.我已经使用 Compiler Tree API (com.sun.source.tree) 走得很远.我可以打印出 Tree 数据类型,同时用新的类名替换旧的.但有些问题似乎很难.

I'd like to copy all top-level import statements and static members and not static members of prototype-class. I've moved pretty far with Compiler Tree API (com.sun.source.tree). I can print out Tree data-type while substituting new class name for old. But there are problems that seems pretty hard.

如果我在树中得到 Tree.Kind.IDENTIFIER,我怎样才能找到它引用的实际类.我需要用 MySuperClass 标识符替换所有出现的 MySuperClassPrototype 标识符,然后打印出整个树.

If I get Tree.Kind.IDENTIFIER in the tree, how can I find what actual class it references. I need to replace all occurrences of MySuperClassPrototype identifier with MySuperClass identifier, and than print out whole tree.

可行吗?

同样,我需要过滤掉@MyAnnotation 注释,并再次用 Tree.Kind.IDENTIFIER 或 Tree.Kind.MEMBER_SELECT 表示.

Similarly I need to filter out @MyAnnotation annotation, and again it is represented with Tree.Kind.IDENTIFIER or Tree.Kind.MEMBER_SELECT.

如何找出这个标识符引用的实际注解类?

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

另一个问题是打印出树.如果我使用 toString 方法,我会得到不错的结果,但是构造函数被打印为带有<init>"的方法名称而不是与它的类同名的方法,所以我需要手动打印每种树节点.

And another problem is printing out tree. If I use toString method I got decent result, but constructors are printed as methods with "<init>" name instead of methods with the same name as it's class, so I need to manually print every kind of Tree node.

你可以看到我在这里提供的代码

推荐答案

8 年了还没有回答.正因如此,我会尽力回答,让您满意.

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.

可行吗?

是的,绝对.

如何找出这个标识符引用的实际注解类?

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

您必须在注释处理器中使用 RoundEnvironment 来获取 TypeElement.

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

静态元编程(您要求的)是在编译时完成的元编程.作者:Kontrast:动态元编程是在运行时完成的元编程.元编程本身就是程序的设计,将其他程序作为数据处理.

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,很多内容.如果你对这个话题感兴趣,一个或多或少的好来源是 维基百科.

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

您的目标是在编译时生成一个类.对于运行时,这将使用诸如 cglib 之类的东西来完成.但是,由于您选择静态(并且出于所有正确的原因),我不会解释这一点.

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.

您正在寻找的概念是注释处理器.该链接是指向 Baeldung 的链接,他们在那里完全按照您的要求执行操作,并且只考虑了构建器模式.您会很高兴听到这种情况非常受鼓励,并且可以通过注释处理器 API 轻松实现.它甚至允许您生成代码,然后将其再次传递给同一个或另一个注释处理器,而您无需执行任何操作.

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.

在直接进入之前,请尝试在 google 上搜索有关Java 注释处理"的信息.那里有很多好的资源,它们会对您有所帮助.太多了,在这里列出.请注意,注释处理器中的编码与正常编码不同.差别不大,但您正在处理的类尚未创建.所以请记住这一点,不要气馁!

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!

您的基本注释处理器看起来像这样:

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

AutoService Annotation 用于动态注册您的注释处理器.它来自外部来源,只是为了让您不会想知道为什么这段代码无法编译.

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.

handle all annotated classes 部分,您有带注释的元素(即带注释的类).您现在必须验证它们是类而不是接口或其他注释.这是因为 @Target(ElementType.Type) 是针对任何类型的,包括接口和注解.此外,您需要验证是否存在您需要的任何内容,或者使用 Messager.

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.

如果您在此处打印错误(例如),您将停止编译并且该错误将在大多数现代 IDE 中看到.可以通过调用 roundEnv.getMessager()

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()

之后,您可以生成一个新类并将其作为 .java 文件写入编译器的输入.这可以通过使用 Filer.

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.

StackOverflow 中的一个答案对这个话题确实不公平.我强烈建议查看 Baeldung 示例和试图从那里发现事情.这个 API 与 Java 6 一样古老,但仍然没有被广泛使用.我鼓励你,读者,自己尝试一下:)

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 :)

这篇关于Java静态元编程的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆