diff --git a/examples/misc/lib/language_tour/classes/enum.dart b/examples/misc/lib/language_tour/classes/enum.dart index 4f360c1703..a589a7b099 100644 --- a/examples/misc/lib/language_tour/classes/enum.dart +++ b/examples/misc/lib/language_tour/classes/enum.dart @@ -41,3 +41,26 @@ void main() { print(Color.blue.name); // 'blue' // #enddocregion name } + +// #docregion enhanced +enum Vehicle implements Comparable { + car(tires: 4, passengers: 5, carbonPerKilometer: 400), + bus(tires: 6, passengers: 50, carbonPerKilometer: 800), + bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0); + + const Vehicle({ + required this.tires, + required this.passengers, + required this.carbonPerKilometer, + }); + + final int tires; + final int passengers; + final int carbonPerKilometer; + + int get carbonFootprint => (carbonPerKilometer / passengers).round(); + + @override + int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint; +} +// #enddocregion enhanced diff --git a/examples/misc/lib/samples/spacecraft.dart b/examples/misc/lib/samples/spacecraft.dart index 09bc73026f..d72ff78d28 100644 --- a/examples/misc/lib/samples/spacecraft.dart +++ b/examples/misc/lib/samples/spacecraft.dart @@ -86,3 +86,38 @@ abstract class Describable { print('========='); } } +// #enddocregion abstract + +// #docregion simple-enum +enum PlanetType { terrestrial, gas, ice } +// #enddocregion simple-enum + +// #docregion enhanced-enum +/// Enum that enumerates the different planets in our solar system +/// and some of their properties. +enum Planet { + mercury(planetType: PlanetType.terrestrial, moons: 0, hasRings: false), + venus(planetType: PlanetType.terrestrial, moons: 0, hasRings: false), + // #enddocregion enhanced-enum + earth(planetType: PlanetType.terrestrial, moons: 1, hasRings: false), + mars(planetType: PlanetType.terrestrial, moons: 2, hasRings: false), + jupiter(planetType: PlanetType.gas, moons: 80, hasRings: true), + saturn(planetType: PlanetType.gas, moons: 83, hasRings: true), + // #docregion enhanced-enum + uranus(planetType: PlanetType.ice, moons: 27, hasRings: true), + neptune(planetType: PlanetType.ice, moons: 14, hasRings: true); + + /// A constant generating constructor + const Planet( + {required this.planetType, required this.moons, required this.hasRings}); + + /// All instance variables are final + final PlanetType planetType; + final int moons; + final bool hasRings; + + /// Enhanced enums support getters and other methods + bool get isGiant => + planetType == PlanetType.gas || planetType == PlanetType.ice; +} +// #enddocregion enhanced-enum diff --git a/examples/misc/pubspec.yaml b/examples/misc/pubspec.yaml index f1c946ede8..368095688d 100644 --- a/examples/misc/pubspec.yaml +++ b/examples/misc/pubspec.yaml @@ -2,7 +2,7 @@ name: examples description: dart.dev example code. environment: - sdk: ">=2.16.0 <3.0.0" + sdk: ">=2.17.0-0 <3.0.0" dependencies: args: ^2.3.0 diff --git a/examples/misc/test/samples_test.dart b/examples/misc/test/samples_test.dart index cc8229130a..93818d5738 100644 --- a/examples/misc/test/samples_test.dart +++ b/examples/misc/test/samples_test.dart @@ -140,6 +140,20 @@ void main() { ])); }); + test('use enum', () { + void testIsGiant() { + // #docregion use-enum + final yourPlanet = Planet.earth; + + if (!yourPlanet.isGiant) { + print('Your planet is not a "giant planet".'); + } + // #enddocregion use-enum + } + + expect(testIsGiant, m.prints('Your planet is not a "giant planet".')); + }); + test('extends', () { final o = Orbiter('O', someDate, 42); expect(o.launchYear, someDate.year); diff --git a/src/_guides/language/language-tour.md b/src/_guides/language/language-tour.md index a859f195b1..3bd45545cf 100644 --- a/src/_guides/language/language-tour.md +++ b/src/_guides/language/language-tour.md @@ -3333,7 +3333,7 @@ abstract class AbstractContainer { } ``` - + ### Implicit interfaces Every class implicitly defines an interface containing all the instance @@ -3529,9 +3529,16 @@ a fixed number of constant values. {{site.alert.note}} All enums automatically extend the [`Enum`][] class. + They are also sealed, + meaning they cannot be subclassed, implemented, mixed in, + or otherwise explicitly instantiated. + + Abstract classes and mixins can explicitly implement or extend `Enum`, + but unless they are then implemented by or mixed into an enum declaration, + no objects can actually implement the type of that class or mixin. {{site.alert.end}} -#### Using enums +#### Declaring simple enums To declare a simple enumerated type, use the `enum` keyword and @@ -3547,6 +3554,66 @@ enum Color { red, green, blue } to help prevent copy-paste errors. {{site.alert.end}} +#### Declaring enhanced enums + +Dart also allows enum declarations to declare classes +with fields, methods, and const constructors +which are limited to a fixed number of known constant instances. + +To declare an enhanced enum, +follow a syntax similar to normal [classes](#classes), +but with a few extra requirements: + +* Instance variables must be `final`, + including those added by [mixins](#mixins). +* All [generative constructors](#constant-constructors) must be constant. +* [Factory constructors](#factory-constructors) can only return + one of the fixed, known enum instances. +* No other class can be extended as [`Enum`] is automatically extended. +* There cannot be overrides for `index`, `hashCode`, the equality operator `==`. +* A member named `values` cannot be declared in an enum, + as it would conflict with the automatically generated static `values` getter. +* All instances of the enum must be declared + in the beginning of the declaration, + and there must be at least one instance declared. + +Here is an example that declares an enhanced enum +with multiple instances, instance variables, +a getter, and an implemented interface: + + +```dart +enum Vehicle implements Comparable { + car(tires: 4, passengers: 5, carbonPerKilometer: 400), + bus(tires: 6, passengers: 50, carbonPerKilometer: 800), + bicycle(tires: 2, passengers: 1, carbonPerKilometer: 0); + + const Vehicle({ + required this.tires, + required this.passengers, + required this.carbonPerKilometer, + }); + + final int tires; + final int passengers; + final int carbonPerKilometer; + + int get carbonFootprint => (carbonPerKilometer / passengers).round(); + + @override + int compareTo(Vehicle other) => carbonFootprint - other.carbonFootprint; +} +``` + +To learn more about declaring enhanced enums, +see the section on [Classes](#classes). + +{{site.alert.version-note}} + Enhanced enums require a [language version][] of at least 2.17. +{{site.alert.end}} + +#### Using enums + Access the enumerated values like any other [static variable](#static-variables): @@ -3607,14 +3674,8 @@ use the `.name` property: print(Color.blue.name); // 'blue' ``` -Enumerated types have the following limits: - -* You can't subclass, mix in, or implement an enum. -* You can't explicitly instantiate an enum. - -For more information, see the [Dart language specification][]. - + ### Adding features to a class: mixins Mixins are a way of reusing a class's code in multiple class diff --git a/src/samples/index.md b/src/samples/index.md index 82c0e8e20e..494e087ed9 100644 --- a/src/samples/index.md +++ b/src/samples/index.md @@ -218,6 +218,66 @@ including initializer lists, optional `new` and `const`, redirecting constructor `factory` constructors, getters, setters, and much more. +## Enums + +Enums are a way of enumerating a predefined set of values or instances +in a way which ensures that there cannot be any other instances of that type. + +Here is an example of a simple `enum` that defines +a simple list of predefined planet types: + + +```dart +enum PlanetType { terrestrial, gas, ice } +``` + +Here is an example of an enhanced enum declaration +of a class describing planets, +with a defined set of constant instances, +namely the planets of our own solar system. + + +```dart +/// Enum that enumerates the different planets in our solar system +/// and some of their properties. +enum Planet { + mercury(planetType: PlanetType.terrestrial, moons: 0, hasRings: false), + venus(planetType: PlanetType.terrestrial, moons: 0, hasRings: false), + // ยทยทยท + uranus(planetType: PlanetType.ice, moons: 27, hasRings: true), + neptune(planetType: PlanetType.ice, moons: 14, hasRings: true); + + /// A constant generating constructor + const Planet( + {required this.planetType, required this.moons, required this.hasRings}); + + /// All instance variables are final + final PlanetType planetType; + final int moons; + final bool hasRings; + + /// Enhanced enums support getters and other methods + bool get isGiant => + planetType == PlanetType.gas || planetType == PlanetType.ice; +} +``` + +You might use the `Planet` enum like this: + + +```dart +final yourPlanet = Planet.earth; + +if (!yourPlanet.isGiant) { + print('Your planet is not a "giant planet".'); +} +``` + +[Read more](/guides/language/language-tour#enums) about enums in Dart, +including enhanced enum requirements, automatically introduced properties, +accessing enumerated value names, switch statement support, and much more. + + ## Inheritance Dart has single inheritance.