Dynamic is a scripting-friendly class that bypasses most member access checks (existence, typing, visibility) during runtime.
In the following example, we first create a Dynamic object using its default constructor, then dynamically add and update a few "fields", which, in the parlance of Julian, are referred to as properties:Dynamic dynObj = new Dynamic();
dynObj.tag = "test";
dynObj.doubleFun = (int x) => x * 2;
Console.println(dynObj.tag); // print "test"
Console.println(dynObj.doubleFun(10)); // print "10"
dynObj.tag = 25;
Console.println(dynObj.tag); // print "25"
As this example shows, the properties can be accessed and mutated without definition. This behavior remarkably constrasts to the regular class object where all the class members must be defined in the class body. In fact, JSE treats Dynamic object in a special way when it comes to member access. It won't check the member's existence, and will disregard the type of the existing value if it's to be overwritten. The regular members inherited from the parent class (Object) or implemented by Dynamic itself, such as toString and getIterator, cannot be overwritten by a property.
Properties can't be accessed via reflection, for they are not class members.
Since Dynamic implements System.Util.IMapInitializable, one may also create the object with initial properties in a single statement:Dynamic dynObj = new Dynamic(){
"tag" = "test",
doubleFun = (int x) => x * 2
};
The left side of =
can have a few different forms: single quoted char ('a'
), double quoted string ("key"
), an identifier or an expression encapsulated in parentheses. IMapInitializable would usually accept more forms of the left side than these four, but Dynamic is special and would treat other kinds of key that are totally legal in, say System.Collection.Map, as input error.
In the case of char
type, the engine will coerce it into a string. For identifier, the engine also re-interprets it as a string. This is true even if in the current scope there is actually a defined identifier of the same name that happens to represent a string value. If a variable has to be used to provide the key, one may enclose it with parentheses, so that the entire left side will be evaluated as an expression, as demonstrated in the ensuing example:string key_a = "nam_a",
Dynamic dynObj = new Dynamic(){
key_a = "val0", // key_a treated as "key_a"
key_b = "val1", // key_b treated as "key_b"
(key_a) = "val2", // key_a evaluated to "nam_a"
};
Console.println(dynObj.key_a); // val0
Console.println(dynObj.key_b); // val1
Console.println(dynObj.nam_a); // val2
Since all keys in Dynamic must be of type string, failing to provide a string-typed key will result in System.Lang.RuntimeCheckException.
Dynamic implements System.Util.IIndexable, such that the members can be retrieved using standard index syntax or the underlying access methods. This alternative is useful when the key is not a legal identifier.dynObj["tag"] = "test";
var tag = dynObj.at("tag");
Dynamic also implements System.Util.IIterable, so enumerating its consituent members is supported at the language level.for (var entry : dynObj){
Console.println(entry.key + "=" + entry.value);
}
There are still a few subtleties surrounding the practical usage of Dynamic class: what would happen if we access to a property that doesn't exist (the default behavior is to return null
); what this
means inside a lambda that is set to a Dynamic object (the default behavior is to bind with the dynamic object itself, if it has not been bound yet). These behaviors, however, can be fine-tuned using the second constructor.
For more information about Dynamic class and its usage, refer to the tutorial.
Parent Class
Parent Interfaces
Type | Name | Signature |
---|---|---|
constructor | Dynamic | public Dynamic() |
constructor | Dynamic | public Dynamic(Dynamic) |
method | at | public var at(var) |
method | at | public void at(var, var) |
method | getIterator | public System.Util.IIterator getIterator() |
method | initByMap | public void initByMap(System.Util.Entry[]) |
method | size | public int size() |
public Dynamic()
Create a new Dynamic instance with default behavior.
The default behavior refers to: returning null
when accessing to an unset member; no automatic binding to this
argument.
public Dynamic(Dynamic config)
Create a new Dynamic instance with specified behavior. The argument is itself a Dynamic object. All accepted properties are of bool
type with default value = false
.
this
appearing in the function body of a Function object to the current Dynamic object, regardless of the function's kind (lambda, member method, global function). If not set, only literal lambda will be bound.Parameters
public var at(var key)
Get the value by key of string type. This is equivalent to retrieving the member by direct access using addressing syntax.
This is the implementation of getter method on System.Util.IIndexble.
Parameters
Returns
Throws
throwIfNotExist == true
.public void at(var key, var value)
Set the value by key of string type.
Parameters
public IIterator getIterator()
Get an iterator from this dynamic object.
As the implementation of System.Util.IIterable, this method returns an iterator of key-value pairs. The iterator will be immediately populated with all the dynamic properties which have been set at the timing of invocation. Beware the implications of this behavior: (1) It's not a scalable method. But the programmer is not advised to use a dyanmic object as a massive data storage in the first place; (2) It's not showing the up-to-date data contained in an dynamic object, but as an upside it's not affected by concurrent modification either.
Members which are not added through the indexable API, or its syntax sugar such as index-based or direct member access, won't show up in the resulting iterator.
Returns
public void initByMap(Entry[] entries)
Initliaze the dynamic object with an array of key-value pairs.
Only key of string type is allowed. Null key is not allowed. If duplicated by key, the last entry by the textual order wins. When invoked by the scripting engine as a translation of map initializer (the map-like syntax structure immediately following the constructor call), identifiers and char literals will be re-interpreted as strings, while other key expressions which do not evaluate to a string or char will be considered illegal input, incurring RuntimeCheckException.
Parameters
Throws
public int size()
Get the count of members of this Dynamic instance, ecluding those inherited from the parent class.
This is the implementation of size()
method on System.Util.IIndexble.
Returns