9

我是注释处理和代码生成的新手。我想了解如何执行此类操作,例如将新方法附加到现有类。这是我想做的一个例子:

假设我们有一个带有自定义注释的类,如下所示:

class SourceClass {
    @CustomAnnotation
    fun annotatedFun1(vararg argument: Any) {
        //Do something
    }

    @CustomAnnotation
    fun annotatedFun2(vararg argument: Any) {
        //Do something
    }

    fun someOtherFun() {
        //Do something
    }
}

我想要得到的结果 - 该类的扩展副本:

class ResultClass {
    fun hasFunWithName(name: String): Boolean {
        return (name in arrayOf("annotatedFun1", "annotatedFun2"))
    }

    fun callFunByName(name: String, vararg arguments: Any) {
        when (name) {
            "annotatedFun1" -> annotatedFun1(*arguments)
            "annotatedFun2" -> annotatedFun2(*arguments)
        }
    }

    fun annotatedFun1(vararg argument: Any) {
        //Do something
    }

    fun annotatedFun2(vararg argument: Any) {
        //Do something
    }

    fun someOtherFun() {
        //Do something
    }
}

我已经找到了如何创建注释处理器。我正在寻找一种方法来保存源类中的所有现有字段、属性和方法,并在其中添加更多方法。

如果可以在不创建新类的情况下修改类 - 这将是完美的,但在所有教程中只创建新类,我没有找到任何将源类的所有内容复制到另一个类的示例。

请不要建议使用反射。我需要这个用于android,所以反射不是资源成本的选择原因。我正在寻找编译时解决方案。

它是在应用程序中实现的自定义脚本语言所必需的,应该用于简化包装类结构。当这项工作直接在代码中完成时 - 当这样的方法计数超过每个类 20 时,它看起来很糟糕。

4

4 回答 4

4

这是我最近使用的 Java 注释处理的一个很好的例子。这是注释的实现。@Immutable

查看 ByteBuddy 或 Kotlin Poet 以了解额外代码生成的工作原理。

对于 Kotlin,您所做的几乎相同,请查看本手册以了解 Kotlin 特定的步骤。

于 2020-12-25T23:56:46.360 回答
1

您可能会遵循Project Lombok使用的模式。请参阅龙目岛如何工作?源代码了解详情。

另一种选择是编写一个扩展源类的新类:

class ResultClass : SourceClass {
    fun hasFunWithName(name: String): Boolean {
        return (name in arrayOf("annotatedFun1", "annotatedFun2"))
    }

    fun callFunByName(name: String, vararg arguments: Any) {
        when (name) {
            "annotatedFun1" -> annotatedFun1(*arguments)
            "annotatedFun2" -> annotatedFun2(*arguments)
        }
    }
}

或者也许使用组合代替并为SourceClass.

如果您不依赖于使用注释处理来执行此操作,则可以在编译之前使用单独的一段自定义代码来处理源代码文件。也许使用正则表达式/@CustomAnnotation\s+.*fun (\w+)\s*\(([^)]*)\)/gm(例如Test on Regex101)来查找带注释的方法。

于 2020-12-25T00:34:09.250 回答
1

如果我正确理解了要求,那么目标是实现如下所述的内容。

您有一个C.java定义类的源文件,C如下所示:

public final class C
{
    @Getter
    @Setter
    private int m_IntValue;

    @Getter
    @Constructor
    private final String m_Text;
}

现在您想知道如何编写一个注释处理器,该处理器在编译期间跳转并修改C.java编译器看到的源代码,如下所示:

public final class C
{
    private int m_IntValue;
    public final int getIntValue() { return m_IntValue; }
    public final void setIntValue( final int intValue ) { m_IntValue = intValue; }

    private final String m_Text;
    public final String getText() { return m_Text; }

    public C( final String text ) { m_Text = text; }
}

坏消息是,这是不可能的……注释处理器不行,Java 15 也不行。

对于 Java 8,有一种方法,使用一些带有反射的内部类来说服 AP 以某种方式操作已经加载的源代码,并让编译器对其进行第二次编译。不幸的是,它失败的次数多于工作的次数……</p>

目前,注释处理器只能创建一个新的(在附加的意义上)源文件。因此,一种解决方案可能是扩展类(当然,这不适用于C上面的示例类,因为类本身是最终的,并且所有属性都是私有的……</p>

因此编写预处理器将是另一种解决方案;C.java您的硬盘驱动器上没有文件,但是C.myjava该预处理器将使用该文件来生成一个名为的文件C.java,然后编译器将使用该文件。但这不是由注释处理器完成的,但有可能以这种方式滥用它。

您还可以使用编译器生成的字节码,并在那里添加缺少的(或附加的)功能。但这与注释处理相去甚远……</p>


总结一下:今天(从 Java 15 开始),注解处理器不允许操作现有源代码(您甚至不能排除某些源代码正在编译);您只能使用注释处理器生成其他源文件。

于 2020-12-29T01:33:19.710 回答
1

使用 Kotlin,您可以使用扩展函数,这是向您无法控制的现有类添加新功能的推荐方式。https://kotlinlang.org/docs/reference/extensions.html

于 2018-05-10T21:44:04.733 回答