Mapped Types

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 mapped keyword.

Please refer to the Mapped Types topic in the Language Concepts section for more details.

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 to directly to the original class. Both mapped classes and mapped records can provide an optional list of Interfaces that they adhere to.

Members

Mapped types can define members such as Constants, 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 an different, existing class, mapped typed 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 thru mapped. mapped can also be used standalone, to refer to the current instance as it's 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

Shortcut Mappings

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
    property MyLength: Integer; mapped to Length;
  end;

Here the MyLength property is mapped directly to the Length property 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 "as as" on the mapped type.

type
  MyString = public class mapped to String
    property Length: Integer; mapped to Length;
  end;

Constructors

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 opy of the original type, constructors in mapped types have soe additional capabilities.

In addition to defering 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 exit.

constructor MyString(aChar: Char);
begin
  result := aChar as String;
end;

It is also possible to use the mapped Expression to defer to constructors of the original type. This works in symmetry with how the inherited constructor Syntax works in "real" classes:

constructor MyObject;
begin
  mapped constructor("Hello");
end;

Type Visibility

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 assembly.

Other Modifiers

A mapped type is a Class or Record that is marked with the mapped to Type Modifier.

See Also