如何使用 java / kotlin 中的注释处理将方法附加到现有 class?
How to append a method to existing class using annotation processing in java / kotlin?
我不熟悉注释处理和代码生成。我想了解如何执行此类操作,例如将新方法附加到现有 class。这是我想要做的一个例子:
假设我们有一个 class 带有像这样的自定义注释:
class SourceClass {
@CustomAnnotation
fun annotatedFun1(vararg argument: Any) {
//Do something
}
@CustomAnnotation
fun annotatedFun2(vararg argument: Any) {
//Do something
}
fun someOtherFun() {
//Do something
}
}
我想要得到的结果 - class 的扩展副本:
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
}
}
我已经了解了如何创建注释处理器。我正在寻找一种方法来保存源 class 中所有现有的字段、属性和方法,并向其添加更多方法。
如果可以修改 class 而无需创建新的 - 这将是完美的,但在所有教程中只创建了新的 classes 而我没有找到任何示例源 class 的内容正在复制到另一个。
请不要使用反射。 android 我需要这个,所以反射不是资源成本的选择原因。我正在寻找编译时解决方案。
在应用程序中实现的自定义脚本语言是必需的,应该用于简化包装器 classes 结构。当这项工作直接在代码中完成时 - 当这种方法计数超过 20 个时,看起来很糟糕 class.
借助 Kotlin,您可以使用扩展函数,这是向您无法控制的现有 类 添加新功能的推荐方法。 https://kotlinlang.org/docs/reference/extensions.html
您可以按照 Project Lombok. See How does lombok work? or the source code 使用的模式了解详细信息。
另一种选择是编写一个新的 class 来扩展您的源代码 class:
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
中的所有 public 方法实现覆盖方法。
如果您不依赖于使用注释处理来执行此操作,则可以在编译之前使用一段单独的自定义代码来处理源代码文件。也许使用像 /@CustomAnnotation\s+.*fun (\w+)\s*\(([^)]*)\)/gm
(Test on Regex101) 这样的正则表达式来查找带注释的方法。
这是我最近使用的 good example 的 Java 注释处理。
这是 @Immutable
个注释的 an implementation。
查看 ByteBuddy 或 Kotlin Poet 以了解附加代码生成的工作原理。
对于 Kotlin,您的操作几乎相同,请检查 this manual 了解 Kotlin 特定的步骤。
如果我对要求的理解正确,目标是实现如下所述的内容。
您有一个源文件 C.java
定义 class 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 不行。
对于 Java8 有一种方法,使用一些带有反射的内部 classes 来说服 AP 以某种方式操作已经加载的源代码并让编译器再次编译它时间。不幸的是,它失败的次数多于成功的次数……
目前,注释处理器只能创建一个新的(附加意义上的)源文件。因此,一种解决方案可能是扩展 class(当然,这对上面的示例 class C
不起作用,因为 class 本身是最终的,所有属性是私人的……
所以编写预处理器将是另一种解决方案;您的硬盘驱动器上没有文件 C.java
,而是一个名为 C.myjava
的文件,预处理器将使用该文件生成 C.java
,而编译器又会使用该文件。但这不是由注释处理器完成的,但可能会以这种方式滥用它。
您还可以使用编译器生成的字节码并在其中添加缺少的(或附加的)功能。但这离注释处理真的很远……
总结一下:今天(截至 Java 15),注解处理器不允许对现有源代码进行操作(您甚至不能排除某些源代码不被编译);您只能使用注释处理器生成额外的源文件。
我不熟悉注释处理和代码生成。我想了解如何执行此类操作,例如将新方法附加到现有 class。这是我想要做的一个例子:
假设我们有一个 class 带有像这样的自定义注释:
class SourceClass {
@CustomAnnotation
fun annotatedFun1(vararg argument: Any) {
//Do something
}
@CustomAnnotation
fun annotatedFun2(vararg argument: Any) {
//Do something
}
fun someOtherFun() {
//Do something
}
}
我想要得到的结果 - class 的扩展副本:
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
}
}
我已经了解了如何创建注释处理器。我正在寻找一种方法来保存源 class 中所有现有的字段、属性和方法,并向其添加更多方法。
如果可以修改 class 而无需创建新的 - 这将是完美的,但在所有教程中只创建了新的 classes 而我没有找到任何示例源 class 的内容正在复制到另一个。
请不要使用反射。 android 我需要这个,所以反射不是资源成本的选择原因。我正在寻找编译时解决方案。
在应用程序中实现的自定义脚本语言是必需的,应该用于简化包装器 classes 结构。当这项工作直接在代码中完成时 - 当这种方法计数超过 20 个时,看起来很糟糕 class.
借助 Kotlin,您可以使用扩展函数,这是向您无法控制的现有 类 添加新功能的推荐方法。 https://kotlinlang.org/docs/reference/extensions.html
您可以按照 Project Lombok. See How does lombok work? or the source code 使用的模式了解详细信息。
另一种选择是编写一个新的 class 来扩展您的源代码 class:
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
中的所有 public 方法实现覆盖方法。
如果您不依赖于使用注释处理来执行此操作,则可以在编译之前使用一段单独的自定义代码来处理源代码文件。也许使用像 /@CustomAnnotation\s+.*fun (\w+)\s*\(([^)]*)\)/gm
(Test on Regex101) 这样的正则表达式来查找带注释的方法。
这是我最近使用的 good example 的 Java 注释处理。
这是 @Immutable
个注释的 an implementation。
查看 ByteBuddy 或 Kotlin Poet 以了解附加代码生成的工作原理。
对于 Kotlin,您的操作几乎相同,请检查 this manual 了解 Kotlin 特定的步骤。
如果我对要求的理解正确,目标是实现如下所述的内容。
您有一个源文件 C.java
定义 class 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 不行。
对于 Java8 有一种方法,使用一些带有反射的内部 classes 来说服 AP 以某种方式操作已经加载的源代码并让编译器再次编译它时间。不幸的是,它失败的次数多于成功的次数……
目前,注释处理器只能创建一个新的(附加意义上的)源文件。因此,一种解决方案可能是扩展 class(当然,这对上面的示例 class C
不起作用,因为 class 本身是最终的,所有属性是私人的……
所以编写预处理器将是另一种解决方案;您的硬盘驱动器上没有文件 C.java
,而是一个名为 C.myjava
的文件,预处理器将使用该文件生成 C.java
,而编译器又会使用该文件。但这不是由注释处理器完成的,但可能会以这种方式滥用它。
您还可以使用编译器生成的字节码并在其中添加缺少的(或附加的)功能。但这离注释处理真的很远……
总结一下:今天(截至 Java 15),注解处理器不允许对现有源代码进行操作(您甚至不能排除某些源代码不被编译);您只能使用注释处理器生成额外的源文件。