具有动态字段的 Java 类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3127189/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java classes with dynamic fields
提问by Aaron Digulla
I'm looking for clever ways to build dynamic Java classes, that is classes where you can add/remove fields at runtime. Usage scenario: I have an editor where users should be able to add fields to the model at runtime or maybe even create the whole model at runtime.
我正在寻找构建动态 Java 类的巧妙方法,即您可以在运行时添加/删除字段的类。使用场景:我有一个编辑器,用户应该能够在运行时向模型添加字段,甚至可以在运行时创建整个模型。
Some design goals:
一些设计目标:
- Type safe without casts if possible for custom code that works on the dynamic fields (that code would come from plugins which extend the model in unforeseen ways).
- Good performance (can you beat
HashMap
? Maybe use an array and assign indexes to the fields during setup?) - Field "reuse" (i.e. if you use the same type of field in several places, it should be possible to define it once and then reuse it).
- Calculated fields which depend on the value of other fields
- Signals should be sent when fields change value (no necessarily via the Beans API)
- "Automatic" parent child relations (when you add a child to a parent, then the parent pointer in the child should be set for "free").
- Easy to understand
- Easy to use
- 对于在动态字段上工作的自定义代码,如果可能,键入安全而不强制转换(该代码将来自以不可预见的方式扩展模型的插件)。
- 良好的性能(你能打败
HashMap
吗?也许在设置过程中使用数组并为字段分配索引?) - 字段“重用”(即如果您在多个地方使用相同类型的字段,应该可以定义一次然后重用它)。
- 依赖于其他字段值的计算字段
- 当字段更改值时应发送信号(不一定通过 Beans API)
- “自动”父子关系(当您将子项添加到父项时,子项中的父指针应设置为“自由”)。
- 容易理解
- 便于使用
Note that this is a "think outside the circle" question. I'll post an example below to get you in the mood :-)
请注意,这是一个“跳出圈子思考”的问题。我会在下面发布一个例子来让你有心情:-)
回答by Aaron Digulla
The obvious answer is to use a HashMap
(or a LinkedHashMap
if you care for the order of fields). Then, you can add dynamic fields via a get(String name)
and a set(String name, Object value)
method.
显而易见的答案是使用 a HashMap
(LinkedHashMap
如果您关心字段的顺序,则使用 a )。然后,您可以通过 aget(String name)
和 aset(String name, Object value)
方法添加动态字段。
This code can be implemented in a common base class. Since there are only a few methods, it's also simple to use delegation if you need to extend something else.
这段代码可以在一个公共基类中实现。由于只有几种方法,如果您需要扩展其他东西,使用委托也很简单。
To avoid the casting issue, you can use a type-safe object map:
为避免转换问题,您可以使用类型安全的对象映射:
TypedMap map = new TypedMap();
String expected = "Hallo";
map.set( KEY1, expected );
String value = map.get( KEY1 ); // Look Ma, no cast!
assertEquals( expected, value );
List<String> list = new ArrayList<String> ();
map.set( KEY2, list );
List<String> valueList = map.get( KEY2 ); // Even with generics
assertEquals( list, valueList );
The trick here is the key which contains the type information:
这里的技巧是包含类型信息的键:
TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );
The performance will be OK.
性能会好的。
Field reuse is by using the same value type or by extending the key class of the type-safe object map with additional functionality.
字段重用是通过使用相同的值类型或通过使用附加功能扩展类型安全对象映射的键类。
Calculated fields could be implemented with a second map that stores Future
instances which do the calculation.
计算字段可以使用存储Future
进行计算的实例的第二个映射来实现。
Since all the manipulation happens in just two (or at least a few) methods, sending signals is simple and can be done any way you like.
由于所有操作都只通过两种(或至少几种)方法进行,因此发送信号很简单,可以按您喜欢的任何方式进行。
To implement automatic parent/child handling, install a signal listener on the "set parent" signal of the child and then add the child to the new parent (and remove it from the old one if necessary).
要实现自动父/子处理,请在子项的“设置父项”信号上安装一个信号侦听器,然后将子项添加到新父项(如有必要,将其从旧父项中删除)。
Since no framework is used and no tricks are necessary, the resulting code should be pretty clean and easy to understand. Not using String as keys has the additional benefit that people won't litter the code with string literals.
由于没有使用任何框架,也不需要任何技巧,因此生成的代码应该非常干净且易于理解。不使用 String 作为键还有一个额外的好处,那就是人们不会用字符串文字乱扔代码。
回答by mikera
So basically you're trying to create a new kind of object model with more dynamic properties, a bit like a dynamic language?
所以基本上你是在尝试创建一种具有更多动态属性的新对象模型,有点像动态语言?
Might be worth looking at the source code for Rhino(i.e. Javascript implemented in Java), which faces a similar challenge of implementing a dynamic type system in Java.
可能值得查看Rhino的源代码(即用 Java 实现的 Javascript),它面临着在 Java 中实现动态类型系统的类似挑战。
Off the top of my head, I suspect you will find that internal HashMaps ultimately work best for your purposes.
在我的脑海中,我怀疑您会发现内部 HashMap 最终最适合您的目的。
I wrote a little game (Tyrant - GPL source available) using a similar sort of dynamic object model featuring HashMaps, it worked great and performance was not an issue. I used a few tricks in the get and set methods to allow dynamic property modifiers, I'm sure you could do the same kind of thing to implement your signals and parent/child relations etc.
我使用具有 HashMap 的类似动态对象模型编写了一个小游戏(可用的 Tyrant - GPL 源),它运行良好并且性能不是问题。我在 get 和 set 方法中使用了一些技巧来允许动态属性修饰符,我相信你可以做同样的事情来实现你的信号和父/子关系等。
[EDIT] See the source of BaseObjecthow it is implemented.
[编辑] 查看BaseObject的源代码是如何实现的。
回答by Stephen C
Type safe without casts if possible for custom code that works on the dynamic fields (that code would come from plugins which extend the model in unforeseen ways)
对于在动态字段上工作的自定义代码,如果可能,键入安全而不强制转换(该代码将来自以不可预见的方式扩展模型的插件)
AFAIK, this is not possible. You can only get type-safety without type casts if you use static typing. Static typing means method signatures (in classes or interfaces) that are known at compile time.
AFAIK,这是不可能的。如果您使用静态类型,则只能在没有类型转换的情况下获得类型安全。静态类型意味着在编译时已知的方法签名(在类或接口中)。
The best you can do is have an interface with a bunch of methods like String getStringValue(String field)
, int getIntValue(String field)
and so on. And of course you can only do that for a predetermined set of types. Any field whose type is not in that set will require a typecast.
你能做的最好的事情就是拥有一个包含一堆方法的接口,比如String getStringValue(String field)
,int getIntValue(String field)
等等。当然,您只能对预定的一组类型执行此操作。任何类型不在该集合中的字段都需要类型转换。
回答by MaSEL
You can use the bytecode manipulation libraries for it. Shortcoming of this approach is that you need to do create own classloader to load changes in classes dynamically.
您可以使用字节码操作库。这种方法的缺点是您需要创建自己的类加载器来动态加载类中的更改。
回答by andbi
I do almost the same, it's pure Java solution:
我做的几乎一样,它是纯 Java 解决方案:
- Users generate their own models, which are stored as JAXB schema.
- Schema is compiled in Java classes on the fly and stored in user jars
- All classes are forced to extend one "root" class, where you could put every extra functionality you want.
- Appropriate classloaders are implemented with "model change" listeners.
- 用户生成自己的模型,这些模型存储为 JAXB 模式。
- 模式在 Java 类中即时编译并存储在用户 jar 中
- 所有类都被迫扩展一个“根”类,您可以在其中放置您想要的所有额外功能。
- 适当的类加载器是通过“模型更改”侦听器实现的。
Speaking of performance (which is important in my case), you can hardly beat this solution. Reusability is the same of XML document.
说到性能(这对我来说很重要),你很难击败这个解决方案。可重用性与 XML 文档相同。