使用字节码操作在 Java 中实现委托、事件和按引用传递






4.85/5 (4投票s)
使用字节码操作在 Java 中支持委托、事件和按引用传递
引言
C# 语言已经推出了一些非常棒的功能,而 Java 却缺乏这些功能。人们仍然可以管理,但在 Java 中拥有这些功能会使开发人员的生活更轻松。最近,我发现了 Javassist 库,它能够操作字节码并在运行时编译类。使用它,我能够实现一些功能。
委托
一个委托是一个引用方法的类型。
C# 声明
public delegate int PerformCalculation(int x, int y);
等效的 Java 声明
public abstract class PerformCalculation extends csharp.Delegate {
public abstract int invoke(int x, int y);
}
C# 实例化
PerformCalculation performCalculation = new PerformCalculation(this.myMethod); //instance
PerformCalculation performCalculation = new PerformCalculation(AClass.myMethod); //static
等效的 Java 实例化
PerformCalculation performCalculation = Delegate.<PerformCalculation>
getInstance(PerformCalculation.class, this, "myMethod"); //instance
PerformCalculation performCalculation = Delegate.<PerformCalculation>
getInstance(PerformCalculation.class, AClass.class, "myMethod"); //static
您只需调用 performCalculation
对象的 invoke 方法即可。对于每个使用的 AClass
类型,都会在运行时创建一个新的委托类(但在应用程序的整个生命周期中只创建一次)。
要求
- 声明的 '
Delegate
' 类和 'invoke
' 方法应为 'public abstract
'。 - '
invoke
' 方法的签名应与 'myMethod
' 函数的签名匹配。 - 应该只存在一个 '
invoke
' 方法。 - 如果 '
myMethod
' 函数是public
或protected
,则会更好,否则会使用反射,这可能会有性能问题。
事件
C# 中的一个 event
是一个类在某个有趣的事情发生在对象上时向该类的客户端提供通知的一种方式。这实际上是委托的列表。
C# 声明/实例化
public event PerformCalculation myEvent;
等效的 Java 声明
public abstract class MyEvent<D extends PerformCalculation> extends csharp.Event<D> {
public abstract int invoke(int x, int y);
}
等效的 Java 实例化
MyEvent<PerformCalculation> performEvent =
Event.<MyEvent<PerformCalculation>> getInstance(MyEvent.class, PerformCalculation.class);
现在,您可以通过调用其 'add
' 方法将 PerformCalculation
委托添加到 performEvent
对象中。调用 performEvent
对象的 invoke 方法,它将调用所有添加的 delegate
的 invoke 方法。
要求
- 声明的 '
Event
' 类和 'invoke
' 方法应为 'public abstract
'。 - '
invoke
' 方法的签名应与要添加的委托的签名匹配。 - 应该只存在一个 '
invoke
' 方法。
按引用传递
C# 中的 ref
关键字导致参数通过引用传递。Java 中没有此功能。这意味着如果将来可以使用,基本类型将通过引用传递,而对象将通过引用的引用传递(默认情况下,传递对象的引用)。
因此,以下内容不会更改原始变量。
list = new ArrayList();
但 'list
' 变量将指向一个新的内存位置。为此,我在代码中包含了一些包装器类(每个基本类型一个,以及一个带有泛型的用于对象的类),这些类可用于传递引用并将其取回。这没什么新东西,只是为了以防有人想使用此功能而将其包含在代码中。
注释
如果您是核心 Java 开发人员,请阅读有关事件和委托的内容。在您实际在代码中使用它们之前,您可能不会欣赏这些功能。
更新
- 使用
class.getCanonicalName
代替class.getName
。后者在内部类的情况下存在问题。 - 无需将动态编译的类写入
filesystem
。
历史
- 2012 年 5 月 15 日:初始版本