可以使用 ASM 向现有 Java 类添加方法吗?

Posted

技术标签:

【中文标题】可以使用 ASM 向现有 Java 类添加方法吗?【英文标题】:Possible to add a method to an existing Java class using ASM? 【发布时间】:2016-06-06 16:24:07 【问题描述】:

我想将一个方法添加到我无法控制的现有类中,例如在 Java8 的 Stream 类中有一个 toList() 方法。问题是这是否可以使用 ASM 完成。大概吧。

我更具体的问题是我是否可以创建一个包含扩展方法的 jar 之类的东西(例如类 Stream)。然后我可以将 jar 添加到我的项目中,并且 toList() 方法在 Java8 Stream 类中变得可见。据我了解,扩展方法只能在运行时或当我有一个可以执行此操作的运行时(如 Groovy、Kotlin、Scala)时编织。我知道有 Lombok 项目,但它只有一个支持相应功能的 eclipse 插件。 IntelliJ 的插件不支持此特定功能。

【问题讨论】:

添加一个方法......显而易见的方法是扩展类,并将该方法添加到子类中 是的,没错。不过,我想要的是类似于 Groovy、Kotlin、Scala、Smalltalk、C# 和其他语言中的扩展方法(子类 String 无济于事),而不必恢复到这些语言中的任何一种。 你不能子类化字符串,因为它是一个不可变的类。但是,您可以创建复合对象 没错,但问题是关于扩展方法而不是子类化。 确实如此,但是您的示例:“更改字符串”基本上根本不是您应该(尝试)做的事情。 【参考方案1】:

我尝试按顺序回答问题:

Q1:我想向我无法控制的现有类添加一个方法 是的。无论原始类是什么,您都可以向现有类添加新方法/删除现有方法。 一个简单的 ASM 示例代码是添加

ClassReader cr = new ClassReader(is);//'is' is inputstream to some bytecode.
ClassWriter cw = new SomeClassWriter(ClassWriter.COMPUTE_FRAMES)
cr.accept(cw, 0);

class SomeClassWriter extends ClassVisitor
    ....
    @Override
    public MethodVisitor visitEnd()
         MethodVisitor mv = cw.visitMethod(...); 
         toListMethodNode.accept(mv). 
         super.visitEnd().
    

Q2:我是否可以创建一个类似于 jar 的东西,其中包含扩展方法(比如类 Stream)

要启用新生成的字节码,您需要通过类加载器加载字节码。与是否打包成jar没有关系,新生成的字节码如果不被类加载器加载是没有用的。 这里的要点是:普通的类加载器不允许两次加载具有相同符号名称的字节码。因此,新的字节码类(使用 toList() 方法)具有相同的类名,您必须在另一个类加载器中加载它。例外是使用 Unsafe.defineAnonymousClass(),我认为你不会使用它。

【讨论】:

以上是关于可以使用 ASM 向现有 Java 类添加方法吗?的主要内容,如果未能解决你的问题,请参考以下文章

向接口引入默认方法真的可以保持向后兼容性吗?

在不破坏现有基类的情况下向具有许多派生类的抽象基类添加新方法

Windows Universal现有应用程序向SQLite表添加新列

向现有 json 模式添加额外字段并即时生成 c# 类

如何在现有方法中添加一些生成的代码

向现有 Python 类添加函数的模式