【问题标题】:Is there a way to add a method to a class definition at runtime? [duplicate]有没有办法在运行时将方法添加到类定义中? [复制]
【发布时间】:2020-11-18 04:50:32
【问题描述】:

有一个更好的问题(链接如下)。我的问题鼓励了糟糕的编码做法,但没有概述这些做法的风险。

Can a Java class add a method to itself at runtime?

原来的问题如下:


有没有办法在运行时将方法添加到类定义中?

例如,假设我有以下界面

public interface Singleton<T> {
    @StaticContract
    T getInstanceStatic();
}

在运行时,我会扫描所有类以查找带有注释“StaticContract”的方法,并将已实现方法的静态版本添加到类定义中。但是,我不知道我将如何去做,或者这是否可能。

在我当前的实现中,如果运行时反射在初始化期间没有找到方法的静态方法,我会抛出 NoSuchMethodError。最大的问题是,如果开发人员不熟悉接口,他们可能不知道他们应该创建静态方法。非静态 getInstanceStatic() 对 Singletons 没有任何意义。它只是作为创建静态方法的提醒。

结合使用反射恢复已擦除类型的能力,这将允许我使用泛型远远超出预期。例如,您将不再需要定义和传递工厂对象。您可以在工厂生成的类中定义方法。

另外,如果在运行时没有办法做到这一点,有没有办法在编译时做到这一点?

【问题讨论】:

  • 请不要破坏您的帖子。提交帖子后,您已将内容授权给整个 Stack Overflow 社区 (under the CC BY-SA license)。根据 SE 政策,任何破坏行为都将被撤销。
  • 除上述内容外,您还收到了一份长而详细的答复,您已接受并称之为“一个漂亮的答复”。试图破坏或从根本上改变问题是对花时间帮助你的人的不尊重。
  • 这不是这里的工作方式。请将原始问题作为路线图留给其他寻找类似问题的人。
  • 版主 - 现在我对 stackoverflow 有了一些经验,我正在尝试删除我的帐户。我只是想在走之前把这个问题弄清楚。
  • 您为什么要删除您的帐户?另外,你为什么要删除这个?答案显然对人们有帮助,那么为什么不把它留给其他人阅读呢?

标签: java


【解决方案1】:

想要的都是可能的!

但不是这样。您的实际问题的答案是一个简单而明确的“不”。但是您不想要您在问题中描述的内容。

让我详细说明。

让我们首先说您可以在运行时添加方法。你不能*,但假设你可以。

那将一事无成;给定:

public class Example implements Singleton<Example> {
    @StaticContract Example getInstanceStatic() { return new Example(); }
}

我们已经在这里看到了问题(这个方法是.. public。它必须是,这是接口的规则。但考虑到你希望这是一个单例,那将是一个非常糟糕的消息)。

但是,让我们继续。这个想法是您希望能够在其他代码中编写:

    Example.instance();

但是 - 怎么做?编译器不会让你这样做,因为方法不存在,如果我们按照你的计划(在运行时添加方法),那么在编译时它永远不会在那里,javac 将拒绝编译它。如果它以某种方式 DID 编译了这个,那么在运行时,你可以使用你的魔术并以某种方式添加这个方法,一切都会好起来的,但这是一个有争议的问题 - 没有将一个类文件与一个字节码编辑器,没有办法获得编译版Example.instance()的类文件。

您不想在运行时添加它

但也许您想在编译时添加它

还有那个?你可以做到的!

策略 #1:龙目岛

Project Lombok 让您可以编写 @UtilityClass,这使其具有单例风格。 Lombok 故意没有@Singleton,因为作为一个概念,单例被普遍认为是糟糕的代码风格。我想你可以 fork lombok 并添加它,如果你必须拥有它。

策略 #2:注释处理器

除了 lombok 之外,注释处理器无法向现有源文件添加内容。但他们可以制造新的!作为磁盘源文件上的实际实际字节给出:

@SingletonizeMe
public class Example {
    Example() {} // without lombok you're going to have to write this yourself to ensure nobody outside of the package can instantiate this...
}

然后你可以写一个注解处理器,这意味着javac会自动生成这个文件:

// generated code
package same.pkg.as.your.example;

public class ExampleUtil {
    public static final Example EXAMPLE_INSTANCE = new Example();
}

并将其作为构建的一部分进行编译,任何包含ExampleUtil.EXAMPLE_INSTANCE 的代码都将直接编译,没有任何抱怨。注释处理器解决了“好吧,也许在运行时这会起作用,但是我如何向javac 解释只是做我想做的事情而不拒绝编译它认为在运行时没有机会工作的代码?”的问题。 /p>

策略#3:依赖注入系统

从 dagger 到 spring 再到 guice,有很多库可以进行“依赖注入”,而且几乎所有库都可以选择注入单例样式。快速浏览一下这 3 个库,一旦您按照他们的快速入门教程进行操作,应该会很明显地了解它们的工作原理。

*) 你会认为答案是肯定的,那就是检测和使用代理技术重新加载类文件的能力。但这是“向类添加方法”吗?不,它不是 - 它正在重新加载一个类,如果您尝试添加任何新成员,这通常不会起作用; VM 中内置的热代码替换技术不允许您更改(或添加或删除)任何签名。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-28
    • 2020-12-27
    相关资源
    最近更新 更多