Java:在运行时扩展类

13

我有能力在编译时扩展一个类,但需要能够使用已经实例化的超类实例在运行时创建此子类的实例。

理论上应该可以做到这一点,因为超类构造函数已在子类构造函数之前调用。

我没有足够的程序访问权限来更改实例化到我的子类,也无法中断原始实例化。

用例:存在一个类X的实例数组。我的代码在加载后运行。我需要重载其中一个实例X的方法,使用我的已经扩展了X的子类Y。父程序仅通过该数组访问对象,因此我想用我的Y实例替换该数组元素,但它需要表现得像最初实例化到该数组中一样。我不能只封装超类实例并转发调用,并且重新实例化超类会有困难。

希望这更清楚了。


3
请重新表述问题。 - Michael Slade
1
你能否提供一个实际的例子,说明你想要完成什么?而不仅仅是你所期望的,但可能不可行的方法。 - millimoose
假设有一行现有的代码 "public static Foo foo = new Foo();",我无法更改它。我有一个类 "public class Bar extends Foo",我想要执行 "foo = new Bar(...)",使得它就像第一行代码是 "public static Foo foo = new Bar()" 一样。 - JAKJ
对于那些只想在运行时扩展类而不是更改现有对象的运行时类型的人,请参见在运行时扩展类 - Theodore Murdock
4个回答

6
为了重申你想要做的事情...
在JVM中,存在一个ClassA的实例。您想动态修改ClassA的类层次结构,使得存在一个叫做ClassB的新类,该类派生自ClassA。然后,您想要实例化ClassB的一个实例,但是它的子类实现应该是现有的ClassA实例的子类实现。就像内存替换一样。
您可能需要查看http://www.jboss.org/javassist。您需要做的是替换ClassLoader,然后确定何时加载ClassA,然后实例化。然后,您需要构造ClassB并返回它。
更新
经过更多的研究,仍然有可能实现您想要的功能。像Eclipse这样的IDE支持在调试过程中热交换方法实现。他们使用Instrumentation API。

http://zeroturnaround.com/blog/reloading_java_classes_401_hotswap_jrebel/

你可以替换方法体,但不能添加或删除方法本身。因此,虽然你无法将类型更改为新类型,但可以完全替换方法实现为你的新实现。


我没有在运行时更改继承关系,只有在超类已经实例化时才实例化子类。 - JAKJ
@JAKJ 答案仍然是一样的。您有一个现有对象,想要将其适配成另一种类型的对象。如果您可以控制这个新对象的初始返回点,那么您就可以进行适配并返回所需的实例。我建议您更新您的问题,说明您的用例,而不是您认为的解决方案。 - Andrew T Finnell
我无法替换类加载器,因为我的类在超类之后加载,也不能使用第三方运行时。这必须在Java内完成。 - JAKJ
那你就自求多福吧。如果你能控制JVM的启动方式,你可以做任何事情。如果不能,那就卡住了。用例是什么?请在原问题中更新需要完成的内容。 - Andrew T Finnell
我没有让每个最终用户安装调试类加载器的选项,但我会接受这个答案,因为它似乎是最接近解决方案的。看起来Java就是无法实现我想要的行为。 - JAKJ
我刚刚在寻找类似项目的解决方案。很遗憾我们无法动态扩展已实例化的类。 - Salsero69

3
我建议使用cglib

cglib是一个功能强大、高性能和高质量的代码生成库,用于在运行时扩展JAVA类和实现接口。

你可以在这里找到一些示例:https://github.com/cglib/cglib/wiki

2
我不知道这是否是一个解决方案,但如果你有

public static Foo foo = new Foo();

如果你想用扩展Foo的Bar替换它,可以将Bar作为Foo的包装器,并使用反射让foo指向你的Bar实例。

public class Foo extends Bar {
  private bar; 
  public Foo(Bar oldInstance) {
     this.bar = oldInstance;
  }
}

并且。
// exception handling ommitted
public static void onStartup() {
   Class fooRefClass = FooRef.class;
   Field fooRef = fooRefClass.getDeclaredField("foo");

   Foo foo = (Foo)fooRef.get(null);
   Bar bar = new Bar(foo);
   fooRef.set(null, bar);

我不确定在你的情况下是否可能实现。

如之前所述,这取决于具体情况。


2

你看过Java代理吗?

这里是Javadoc中的一段代码:

"动态代理类(以下简称代理类)是一个在创建类时在运行时实现指定接口列表的类"


1
看起来代理是用于接口而不是类。 - JAKJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接