Class modifiers (original) (raw)

Contents

Class modifiers control how a class or mixin can be used, both from within its own library, and from outside the library where it's defined.

Modifier keywords come before a class or mixin declaration. For example, writing abstract class defines an abstract class. The full set of modifiers that can appear before a class declaration include:

Only the base modifier can appear before a mixin declaration. The modifiers do not apply to other declarations like enum, typedef, extension, or extension type.

When deciding whether to use class modifiers, consider the intended uses of the class, and what behaviors the class needs to be able to rely on.

No modifier

#

To allow unrestricted permission to construct or subtype from any library, use a class or mixin declaration without a modifier. By default, you can:

To define a class that doesn't require a full, concrete implementation of its entire interface, use the abstract modifier.

Abstract classes cannot be constructed from any library, whether its own or an outside library. Abstract classes often have abstract methods.

a.dart

dart

abstract class Vehicle {
  void moveForward(int meters);
}

b.dart

dart

import 'a.dart';

// Error: Can't be constructed.
Vehicle myVehicle = Vehicle();

// Can be extended.
class Car extends Vehicle {
  int passengers = 4;

  @override
  void moveForward(int meters) {
    // ...
  }
}

// Can be implemented.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

If you want your abstract class to appear to be instantiable, define a factory constructor.

To enforce inheritance of a class or mixin's implementation, use the base modifier. A base class disallows implementation outside of its own library. This guarantees:

You must mark any class which implements or extends a base class as base, final, or sealed. This prevents outside libraries from breaking the base class guarantees.

a.dart

dart

base class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}

b.dart

dart

import 'a.dart';

// Can be constructed.
Vehicle myVehicle = Vehicle();

// Can be extended.
base class Car extends Vehicle {
  int passengers = 4;
  // ...
}

// ERROR: Can't be implemented.
base class MockVehicle implements Vehicle {
  @override
  void moveForward() {
    // ...
  }
}

interface

#

To define an interface, use the interface modifier. Libraries outside of the interface's own defining library can implement the interface, but not extend it. This guarantees:

a.dart

dart

interface class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}

b.dart

dart

import 'a.dart';

// Can be constructed.
Vehicle myVehicle = Vehicle();

// ERROR: Can't be inherited.
class Car extends Vehicle {
  int passengers = 4;
  // ...
}

// Can be implemented.
class MockVehicle implements Vehicle {
  @override
  void moveForward(int meters) {
    // ...
  }
}

abstract interface

#

The most common use for the interface modifier is to define a pure interface. Combine the interface and abstract modifiers for an abstract interface class.

Like an interface class, other libraries can implement, but can't inherit, a pure interface. Like an abstract class, a pure interface can have abstract members.

To close the type hierarchy, use the final modifier. This prevents subtyping from a class outside of the current library. Disallowing both inheritance and implementation prevents subtyping entirely. This guarantees:

Final classes can be extended or implemented within the same library. The final modifier encompasses the effects of base, and therefore any subclasses must also be marked base, final, or sealed.

a.dart

dart

final class Vehicle {
  void moveForward(int meters) {
    // ...
  }
}

b.dart

dart

import 'a.dart';

// Can be constructed.
Vehicle myVehicle = Vehicle();

// ERROR: Can't be inherited.
class Car extends Vehicle {
  int passengers = 4;
  // ...
}

class MockVehicle implements Vehicle {
  // ERROR: Can't be implemented.
  @override
  void moveForward(int meters) {
    // ...
  }
}

To create a known, enumerable set of subtypes, use the sealed modifier. This allows you to create a switch over those subtypes that is statically ensured to be exhaustive.

The sealed modifier prevents a class from being extended or implemented outside its own library. Sealed classes are implicitly abstract.

Subclasses of sealed classes are, however, not implicitly abstract.

The compiler is aware of any possible direct subtypes because they can only exist in the same library. This allows the compiler to alert you when a switch does not exhaustively handle all possible subtypes in its cases:

dart

sealed class Vehicle {}

class Car extends Vehicle {}

class Truck implements Vehicle {}

class Bicycle extends Vehicle {}

// ERROR: Can't be instantiated.
Vehicle myVehicle = Vehicle();

// Subclasses can be instantiated.
Vehicle myCar = Car();

String getVehicleSound(Vehicle vehicle) {
  // ERROR: The switch is missing the Bicycle subtype or a default case.
  return switch (vehicle) {
    Car() => 'vroom',
    Truck() => 'VROOOOMM',
  };
}

If you don't want exhaustive switching, or want to be able to add subtypes later without breaking the API, use the final modifier. For a more in depth comparison, read sealed versus final.

Combining modifiers

#

You can combine some modifiers for layered restrictions. A class declaration can be, in order:

  1. (Optional) abstract, describing whether the class can contain abstract members and prevents instantiation.
  2. (Optional) One of base, interface, final or sealed, describing restrictions on other libraries subtyping the class.
  3. (Optional) mixin, describing whether the declaration can be mixed in.
  4. The class keyword itself.

You can't combine some modifiers because they are contradictory, redundant, or otherwise mutually exclusive:

For further guidance on how class modifiers can be combined, check out the Class modifiers reference.

Unless stated otherwise, the documentation on this site reflects Dart 3.7.3. Page last updated on 2024-12-10. View source or report an issue.