[英]How to append a method to existing class using annotation processing in java / kotlin?
我是注释处理和代码生成的新手。 我想知道如何执行这样的操作,比如将新方法附加到现有类。 这是我想要做的一个例子:
假设我们有一个带有自定义注释的类,如下所示:
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 时,这看起来很糟糕。
使用 Kotlin,您可以使用扩展函数,这是向您无法控制的现有类添加新功能的推荐方法。 https://kotlinlang.org/docs/reference/extensions.html
您可能会遵循Project Lombok使用的模式。 请参阅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 ) 这样的正则表达式来查找带注释的方法。
如果我正确理解了需求,目标是实现如下所述的内容。
您有一个源文件C.java
,它定义了类C
如下所示:
public final class C
{
@Getter
@Setter
private int m_IntValue;
@Getter
@Constructor
private final String m_Text;
}
现在您想知道如何编写一个注释处理器,该处理器在编译期间C.java
并将编译器看到的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 以某种方式操作已经加载的源代码,并让编译器对其进行第二次编译。 不幸的是,它失败的次数比成功的要多……
目前,注释处理器只能创建一个新的(在附加的意义上)源文件。 因此,一种解决方案可能是扩展类(当然,这不适用于上面的示例类C
,因为类本身是最终的,并且所有属性都是私有的......
因此,编写预处理器将是另一种解决方案; 您的硬盘驱动器上没有C.java
文件,但有一个名为C.myjava
,该预处理器将使用该文件生成C.java
,然后编译器会使用该文件。 但这不是由注释处理器完成的,但可能会以这种方式滥用它。
您还可以使用编译器生成的字节码,并在其中添加缺失(或附加)的功能。 但这离注释处理真的很远……
总结:今天(从 Java 15 开始),注解处理器不允许操作现有的源代码(你甚至不能从编译中排除某些源代码); 您只能使用注释处理器生成其他源文件。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.