具有动态字段的Java类

我正在寻找构建动态Java类的聪明方法,即可以在运行时添加/删除字段的类。 使用场景:我有一个编辑器,用户应该能够在运行时向模型添加字段,甚至可以在运行时创建整个模型。

一些设计目标:

  • 如果可能的话,在没有强制转换的情况下键入safe,以用于动态字段的自定义代码(该代码将来自以不可预见的方式扩展模型的插件)。
  • 良好的性能(你能击败HashMap吗?也许在设置过程中使用数组并为字段分配索引?)
  • 字段“重用”(即如果在几个地方使用相同类型的字段,则应该可以定义一次然后重复使用它)。
  • 计算字段取决于其他字段的值
  • 当字段更改值时(不一定通过Beans API),应发送信号
  • “自动”父子关系(当您将子项添加到父项时,子项中的父指针应设置为“free”)。
  • 容易明白
  • 使用方便

请注意,这是一个“在圈外思考”的问题。 我将在下面发布一个例子让你心情愉快:-)

如果可能的话,在没有强制转换的情况下键入safe,以用于动态字段的自定义代码(该代码将来自以不可预见的方式扩展模型的插件)

AFAIK,这是不可能的。 如果使用静态类型,则只能在没有类型转换的情况下获得类型安全性。 静态类型表示在编译时已知的方法签名(在类或接口中)。

您可以做的最好的事情是拥有一个接口,其中包含一系列方法,如String getStringValue(String field)int getIntValue(String field)等。 当然,你只能为预定的一组类型做到这一点。 类型不在该集合中的任何字段都需要进行类型转换。

显而易见的答案是使用HashMap (如果您关心字段的顺序,则使用LinkedHashMap )。 然后,您可以通过get(String name)set(String name, Object value)方法添加动态字段。

此代码可以在公共基类中实现。 由于只有少数几种方法,因此如果需要扩展其他方法,使用委托也很简单。

要避免出现转换问题,可以使用类型安全的对象映射 :

  TypedMap map = new TypedMap(); String expected = "Hallo"; map.set( KEY1, expected ); String value = map.get( KEY1 ); // Look Ma, no cast! assertEquals( expected, value ); List list = new ArrayList (); map.set( KEY2, list ); List valueList = map.get( KEY2 ); // Even with generics assertEquals( list, valueList ); 

这里的技巧是包含类型信息的键:

 TypedMapKey KEY1 = new TypedMapKey( "key1" ); TypedMapKey> KEY2 = new TypedMapKey>( "key2" ); 

性能还可以。

字段重用是通过使用相同的值类型或通过使用其他function扩展类型安全对象映射的键类。

计算字段可以使用第二个映射来实现,该映射存储进行计算的Future实例。

由于所有操作仅在两种(或至少几种)方法中发生,因此发送信号很简单,可以按照您喜欢的方式完成。

要实现自动父/子处理,请在子项的“set parent”信号上安装信号侦听器,然后将子项添加到新父项(如果需要,将其从旧父项中删除)。

由于没有使用框架且不需要任何技巧,因此生成的代码应该非常干净且易于理解。 不使用String作为键具有额外的好处,人们不会使用字符串文字乱丢代码。

所以基本上你是在尝试创建一种具有更多动态属性的新型对象模型,有点像动态语言?

可能值得查看Rhino的源代码(即用Java实现的Javascript),它面临着在Java中实现动态类型系统的类似挑战。

在我的头脑中,我怀疑你会发现内部HashMaps最终最适合你的目的。

我使用类似的HashMaps动态对象模型编写了一个小游戏( Tyrant – GPL源代码 ),它运行良好且性能不是问题。 我在get和set方法中使用了一些技巧来允许动态属性修饰符,我相信你可以做同样的事情来实现你的信号和父/子关系等。

[编辑]查看BaseObject的来源如何实现。

您可以使用字节码操作库。 这种方法的缺点是你需要创建自己的类加载器来动态加载类中的更改。

我做的几乎一样,它是纯Java解决方案:

  1. 用户生成自己的模型,这些模型存储为JAXB模式。
  2. Schema在Java类中即时编译并存储在用户jar中
  3. 所有类都被迫扩展一个“root”类,您可以在其中放置所需的所有额外function。
  4. 使用“模型更改”侦听器实现适当的类加载器。

说到性能(这在我的情况下很重要),你几乎无法击败这个解决方案。 可重用性与XML文档相同。