In Dart, how can an enum implement interfaces and apply mixins, and how do you write generic code bounded by Enum?
answer
- implements and with on the enum
- mixin fields must be final
- T extends Enum in generics
- no static values through T
- concrete classes cannot implement Enum
basics
~20 sAn enum can implement interfaces such as Comparable and apply mixins whose fields are final. Generic code uses T extends Enum to read index and name, but must receive the values list as an argument, since T.values does not compile.
solid answer
~40 sEnums can declare `implements Comparable<OrderStatus>` or any interface, and `with SomeMixin`, as long as every instance field, including those a mixin adds, is `final`. That lets enums join ordinary polymorphism: a `List<Labeled>` can hold enum values alongside other implementers. `Enum` itself is the supertype of every enum, so `T extends Enum` lets a generic function use `index`, `name`, `Enum.compareByIndex` and the `EnumByName` extension on an `Iterable<T>`. Dart has no static members through type parameters, so `T.values` does not compile; pass `List<T> values` in, typically as `OrderStatus.values`. A concrete class cannot implement `Enum`, while an abstract class or interface may, which lets you declare a contract only enums can satisfy.
code
dart · 34 linesabstract interface class Labeled {
String get label;
}
mixin Badge {
String get label;
String get badgeText => label.toUpperCase();
}
enum OrderStatus with Badge implements Labeled, Comparable<OrderStatus> {
pending('Pending', 2),
shipped('Shipped', 1),
delivered('Delivered', 0);
const OrderStatus(this.label, this.priority);
@override
final String label;
final int priority;
@override
int compareTo(OrderStatus other) => priority - other.priority;
}
T parseOr<T extends Enum>(List<T> values, String? raw, T fallback) =>
values.asNameMap()[raw] ?? fallback;
void main() {
final List<Labeled> chips = OrderStatus.values;
print(chips.map((c) => c.label).join(', ')); // Pending, Shipped, Delivered
print([...OrderStatus.values]..sort()); // delivered, shipped, pending by priority
print(OrderStatus.shipped.badgeText); // SHIPPED
print(parseOr(OrderStatus.values, 'lost', OrderStatus.pending)); // OrderStatus.pending
}go deeper
Recall that enums can use implements and with, and that every enum is a subtype of Enum.
Explain the final-field rule for mixins, how T extends Enum unlocks index, name and byName, and why T.values is unavailable.
Design shared widgets and helpers around interfaces or Enum bounds so several enums reuse them without switches or casts.
Decide which cross-cutting contracts belong on enums versus separate mapping layers, keeping shared packages decoupled from UI types.
## Enums are classes, so they join normal type hierarchies An enhanced enum is a class with a fixed set of instances, and it can take part in the type system like other classes, with two restrictions: it cannot `extend` anything, because it implicitly extends `Enum`, and every instance field must be `final`. ## Implementing interfaces - **`implements Comparable<OrderStatus>`** gives the enum a natural order that `list.sort()` uses without a comparator. A typical `compareTo` returns `index - other.index`, or compares a domain field such as a priority. - **Your own interfaces** work too. If `Labeled` declares `String get label`, both an `OrderStatus` enum and a `PaymentMethod` class can implement it, and a widget that shows a list of chips can accept `List<Labeled>` without knowing about either. The enum must provide every member of each interface, usually through its final fields or getters. ## Applying mixins An enum can use `with` to pick up behaviour from a mixin. The one extra rule: **fields added by a mixin must be final too**, because enum values are shared constants. Mixins that only add methods and getters, possibly relying on an abstract getter the enum implements, fit best: ```dart mixin Badge { String get label; String get badgeText => label.toUpperCase(); } ``` ## `Enum` as a supertype `Enum`, from `dart:core`, is implemented by every enum. That makes it useful as a **generic bound**: | With `T extends Enum` you can | Because | |---|---| | read `value.index` and `value.name` | `index` is on `Enum`; `name` is the `EnumName` extension on `Enum` | | call `values.byName(raw)` or `values.asNameMap()` on an `Iterable<T>` | `EnumByName` is an extension on iterables of enums | | sort with `Enum.compareByIndex` or `Enum.compareByName` | they are static generic comparators over `T extends Enum` | What you **cannot** do is reach the enum's static members through the type parameter. Dart has no static member access through type variables, so `T.values` is a compile-time error. Generic helpers therefore take the values list as a parameter: ```dart T parseOr<T extends Enum>(List<T> values, String? raw, T fallback) => values.asNameMap()[raw] ?? fallback; final status = parseOr(OrderStatus.values, json['status'] as String?, OrderStatus.pending); ``` ## Who may implement `Enum` `Enum` is declared as an `abstract interface class` with restrictions: 1. **Non-platform classes cannot extend or mix it in.** Only `enum` declarations get it as a superclass. 2. **Concrete classes cannot implement it.** A regular class that says `implements Enum` is an error. 3. **Abstract classes and interfaces may implement it.** `abstract interface class StatusLike implements Enum { ... }` declares a contract that only enums can satisfy, so a generic bound `T extends StatusLike` accepts enums that also carry your extra members. ## How this differs from a class hierarchy Implementing an interface does not make the enum open: its set of values is still closed, and no one can add a value by implementing the same interface. Other classes can implement `Labeled` alongside it, which is the point, but the enum itself stays a fixed set. And because `==` and `hashCode` cannot be overridden on an enum, values that implement an interface still compare by identity, which keeps them safe as map keys and set members. ## When to reach for these features - A shared chip, dropdown or filter widget that should accept several enums: bound it by an interface such as `Labeled`, or by `Enum` plus a label callback. - A generic parser or storage helper for any enum: `T extends Enum` with the values list passed in. - A natural ordering that differs from declaration order: `implements Comparable` with a `compareTo` over a domain field. ## Common mistakes - Writing `T.values` inside a generic function and expecting it to compile. - Adding a mutable field through a mixin and being surprised the enum is rejected. - Assuming a plain enum already implements `Comparable`; it does not unless you declare it.
- Why does `T.values` not compile inside `void f<T extends Enum>()`?Dart does not allow static member access through a type parameter; a type variable is not a class reference. `values` is a static getter generated on each enum, so there is no way to reach it from `T`. Pass the list in, `f(OrderStatus.values)`, or pass a lookup function.
- How can you declare a contract that only enums can implement?Declare an abstract class or interface that implements `Enum`, for example `abstract interface class StatusLike implements Enum { String get label; }`. Concrete classes cannot implement `Enum`, so only `enum` declarations can satisfy it, and a bound `T extends StatusLike` gets both `index`/`name` and your members.
saying these in an interview costs you the question
- Enums cannot implement interfaces because they already extend Enum.
- Inside a generic function you can call T.values when T extends Enum.
- A regular class can implement Enum to behave like an enum.
- Mixins applied to an enum may add mutable fields.
- Every enum automatically implements Comparable by index.