A generic class is parameterized by a type. Box<T> stores a value of type T and returns the same T from unwrap, with no casts and no dynamic. The same class definition handles many concrete element types.

Program

Play the program to build a Box<String> and a Box<int>, then read both through unwrap.

generic_classes.dart
Replay: real traced execution (multi-file project)
class Box<T> {
  final T value;
  Box(this.value);
  T unwrap() => value;
}

void main() {
  var name = Box<String>('Ada');
  var age = Box<int>(36);
  var message = '${name.unwrap()} is ${age.unwrap()}';
  print(message);
}
  1. name ← constructing Box<String>('Ada')

    7void main() {8  var name = Box<String>('Ada');9  var age = Box<int>(36);
    values this stepconstructing Box<String>('Ada')name
  2. value ← Ada

    2final T value;3Box(this.value);4T unwrap() => value;
    values this stepAdavalueAdathis.value
  3. name ← Box<String>(value: Ada)

    7void main() {8  var name = Box<String>('Ada');9  var age = Box<int>(36);
    values this stepBox<String>(value: Ada)name
  4. age ← constructing Box<int>(36)

    8var name = Box<String>('Ada');9var age = Box<int>(36);10var message = '${name.unwrap()} is ${age.unwrap()}';
    values this stepconstructing Box<int>(36)age
  5. value ← 36

    2final T value;3Box(this.value);4T unwrap() => value;
    values this step36value36this.value
  6. age ← Box<int>(value: 36)

    8var name = Box<String>('Ada');9var age = Box<int>(36);10var message = '${name.unwrap()} is ${age.unwrap()}';
    values this stepBox<int>(value: 36)age
  7. return ← Ada

    3  Box(this.value);4  T unwrap() => value;5}
    values this stepAdareturnAdavalue
  8. return ← 36

    3  Box(this.value);4  T unwrap() => value;5}
    values this step36return36value
  9. message ← Ada is 36

    9var age = Box<int>(36);10var message = '${name.unwrap()} is ${age.unwrap()}';11print(message);
    values this stepAda is 36messageAdaname.unwrap()36age.unwrap()
  10. print(message);

    10  var message = '${name.unwrap()} is ${age.unwrap()}';11  print(message);12}
    outputAda is 36
    values this stepAda is 36message
type parameter `class Box<T>` declares a type parameter `T`; each `Box` instance picks a concrete type at construction.
typed field `final T value;` stores exactly the chosen `T`, with no casting or boxing into `Object`.
typed return `T unwrap()` returns the same `T` the caller put in: `Box<String>.unwrap()` is a `String`, `Box<int>.unwrap()` is an `int`.