Mapped Types are a unique feature of the Elements compiler that let you create compatibility wrappers for types without ending up with classes that contain the real type. The wrappers will be eliminated by the compiler and rewritten to use the type the mapping maps to.
Note: When working with Oxygene, you will most commonly use mapped types (for example as provided by the Elements RTL cross-platform library). Using mapped types is seamless, and they behave just like regular non-mapped types.
You will not often need to implement mapped types yourself, but for when you do, Oxygene – like RemObjects C#, Swift and Java – provides a syntax for implementing mapped types when needed, with the
A mapped type can be a Class or a Record, and is declared like any other class or record, but with the
mapped to keywords following the declaration, alongside of the type that is being mapped – also referred to as the "original type".
type MyString = public class mapped to String end;
Mapped Classes can optionally provide an ancestor, as long as that ancestor is either also an ancestor of the original class, or is in itself a mapped class, mapped to an ancestor of or directly to the original class. Both mapped classes and mapped records can provide an optional list of Interfaces that they adhere to.
Mapped types can define members such as Constants and Properties, as well as actions that work with that data (Methods), just like regular classes and records. However, because at runtime mapped classes are, well, mapped to a different, existing class, mapped types cannot add Fields, or properties with implicit storage (which would require an implicit field to be added).
Inside the code of the members of a mapped class (method bodies, property getters and setters), a special
mapped Expression can be used to refer to members of the original type, or the "self" of the original type.
type MyString = public class mapped to String property TwiceTheLength: Integer read mapped.Length*2; end;
Without dereferencing via
mapped, code inside a mapped class sees only the members defined on the mapped class. Members of the original class are available only through
mapped can also be used standalone, to refer to the current instance as its original type.
You can think of
mapped as equivalent to
self – both refer to the same physical instance of the type, but they differ in as what type the class or record is seen.
var x := self; // `x` is a `MyString` var y := mapped; // `y` is a String if x = y then ... // but they are the same
For methods, oxygene supports a special syntax for direct one-to-one mappings of members, using the
mapped to Member Modifier:
type MyString = public class mapped to String method MakeUpper: Integer; mapped to ToUpper; end;
MakeUpper method is mapped directly to the
ToUpper method of the underlying original class.
Note that while in this example the name of mapped and original member differ, it is also acceptable (and common) to map members with the same name, in order to expose the original member on the mapped type, "as as".
type MyString = public class mapped to String method ToUpper: Integer; mapped to ToUpper; end;
Mapped types can provide Constructors that can be used to instantiate copies of the mapped type. Because instantiating a mapped type, ultimately, must end up with instantiating a copy of the original type, constructors in mapped types have some additional capabilities.
In addition to deferring execution to other constructors using regular
constructor Expressions, constructors in mapped types are also allowed to instantiate a copy via any other means (say by calling class factory methods), and returning an instance by assigning to
result or calling
constructor MyString(aChar: Char); begin result := aChar as String; end;
constructor MyObject; begin mapped constructor("Hello"); end;
Just as with regular Classes and Records, the visibility of a mapped type can be controlled by applying a Visibility Modifier on the declaration. A mapped type cannot be more visible than the underlying original type. The default visibility is