Appearance
Record abstract final
abstract final class RecordA record value.
The Record class is a supertype of all record types, but is not itself the runtime type of any object instances (it's an abstract class). All objects that implement Record has a record type as their runtime type.
A record value, described by a record type, consists of a number of fields, which are each either positional or named.
Record values and record types are written similarly to argument lists and simplified function type parameter lists (no required modifier allowed, or needed, since record fields are never optional). Example:
dart
(int, String, {bool isValid}) triple = (1, "one", isValid: true);is syntactically similar to
dart
typedef F = void Function(int, String, {bool isValid});
void callIt(F f) => f(1, "one", isValid: true);Every record and record type has a shape, given by the number of positional fields and the names of named fields. For example:
dart
(double value, String name, {String isValid}) another = (
3.14, "Pi", isValid: "real");is another record declaration with the same shape (two positional fields, one named field named isValid), but with a different type. The names written on the positional fields are entirely for documentation purposes, they have no effect on the program (same as names on positional parameters in function types, like typedef F = int Function(int value);, where the identifier value has no effect).
Record values are mainly destructured using patterns, like:
dart
switch (triple) {
case (int value, String name, isValid: bool ok): // ....
}The individual fields can also be accessed using named getters, using $1, $2, etc. for positional fields, and the names themselves for named fields.
dart
int value = triple.$1;
String name = triple.$2;
bool ok = triple.isValid;Because of that, some identifiers cannot be used as names of named fields:
- The names of
Objectmembers:hashCode,runtimeType,toStringandnoSuchMethod. - The name of a positional getter in the same record, so
(0, $1: 0)is invalid, but(0, $2: 0)is valid, since there is no positional field with getter$2in that record shape. (It'll still be confusing, and should be avoided in practice.) - Also, no name starting with an underscore,
_, is allowed. Field names cannot be library private.
The run-time type of a record object is a record type, and as such, a subtype of Record, and transitively of Object and its supertypes.
Record values do not have a persistent identical behavior. A reference to a record object can change at any time to a reference to another record object with the same shape and field values.
Other than that, a record type can only be a subtype of another record type with the same shape, and only if the former record type's field types are subtypes of the other record type's corresponding field types. That is, (int, String, {bool isValid}) is a subtype of (num, String, {Object isValid}), because they have the same shape, and the field types are pointwise subtypes. Record types with different shapes are unrelated to each other.
Properties
hashCode no setter override
int get hashCodeA hash-code compatible with ==.
Since operator== is defined in terms of the == operators of the record's field values, the hash code is also computed based on the Object.hashCode of the field values.
There is no guaranteed order in which the hashCode of field values is accessed. It's unspecified how those values are combined, other than it being consistent throughout a single program execution.
Implementation
dart
int get hashCode;runtimeType no setter override
Type get runtimeTypeA Type object representing the runtime type of a record.
The runtime type of a record is defined by the record's shape, the number of positional fields and names of named fields, and the runtime type of each of those fields. (The runtime type of the record does not depend on the runtimeType getter of its fields' values, which may have overridden Object.runtimeType.)
The Type object of a record type is only equal to another Type object for a record type, and only if the other record type has the same shape, and if the corresponding fields have the same types.
Implementation
dart
Type get runtimeType;Methods
noSuchMethod() inherited
dynamic noSuchMethod(Invocation invocation)Invoked when a nonexistent method or property is accessed.
A dynamic member invocation can attempt to call a member which doesn't exist on the receiving object. Example:
dart
dynamic object = 1;
object.add(42); // Statically allowed, run-time errorThis invalid code will invoke the noSuchMethod method of the integer 1 with an Invocation representing the .add(42) call and arguments (which then throws).
Classes can override noSuchMethod to provide custom behavior for such invalid dynamic invocations.
A class with a non-default noSuchMethod invocation can also omit implementations for members of its interface. Example:
dart
class MockList<T> implements List<T> {
noSuchMethod(Invocation invocation) {
log(invocation);
super.noSuchMethod(invocation); // Will throw.
}
}
void main() {
MockList().add(42);
}This code has no compile-time warnings or errors even though the MockList class has no concrete implementation of any of the List interface methods. Calls to List methods are forwarded to noSuchMethod, so this code will log an invocation similar to Invocation.method(#add, [42]) and then throw.
If a value is returned from noSuchMethod, it becomes the result of the original invocation. If the value is not of a type that can be returned by the original invocation, a type error occurs at the invocation.
The default behavior is to throw a NoSuchMethodError.
Inherited from Object.
Implementation
dart
@pragma("vm:entry-point")
@pragma("wasm:entry-point")
external dynamic noSuchMethod(Invocation invocation);toString() override
String toString()Creates a string-representation of the record.
The string representation is only intended for debugging, and may differ between development and production. There is no guaranteed format in production mode.
In development mode, the string will strive to be a parenthesized comma separated list of field representations, where the field representation is the toString of the value for positional fields, and someName: followed by that for a named field named someName.
Implementation
dart
String toString();Operators
operator ==() override
Checks whether other has the same shape and equal fields to this record.
A record is only equal to another record with the same shape, and then only when the value of every field is equal, occording to its ==, to the corresponding field value of other.
There is no guaranteed order in which field value equality is checked, and it's unspecified whether further fields are checked after finding corresponding fields which are not equal. It's not even guaranteed that the order is consistent within a single program execution.
As usual, be very careful around objects which break the equality contract, like double.nan which is not equal to itself. For example
dart
var pair = ("a", double.nan);
if (pair != pair) print("Oops");will print the "Oops", because pair == pair is defined to be equal to "a" == "a" & double.nan == double.nan, which is false.
Implementation
dart
bool operator ==(Object other);