Dynamic Objects

Dynamic Objects are the one and only dynamic feature in Marcel. They allow you to manipulate any kind of objects using dynamic properties and method calls.

What does it do

DynamicObject (or dynobj) is an interface that is handled specially by the Marcel compiler. All field access, method calls and operator uses on a DynamicObject are resolved at runtime instead of compile-time.

The DynamicObject wraps an actual (and non dynamic) object. Various types are handled in order to make them easy to manipulate through the DynamicObject API. For example, you can manipulate maps like objects with properties.

dynobj dMap = [foo: 'bar', zoo: 'pew'] as dynobj

println(dMap.foo)
dMap.zoo = 8

Note that dynamic method calls won't be applicable for all methods of the actual object wrapped by the dynobj, this feature is limited. And if you attempt to call a method that isn't defined/handled, you will get an error at runtime.

dynobj o = 1
println(o[1]) // will throw MissingMethodException at runtime, instead of a semantic error at compile time

The same behaviour applies for field access.

Register fields/methods

Dynamic Objects allow you to register method/fields to specific instances. Use the registerMethod/registerField methods for that.

dynobj o = 1

o.registerMethod("foo", Integer.class) { Integer i -> i * 2 + 1 }
println(o.foo(1)) // 3

o.registerField("bar", "value")
println(o.bar) // value
o.bar = "new value"
println(o.bar) // new value