Skip to main content
An overridden setter is a polymorphism mechanism in Dart where a subclass redefines the implementation of a property mutator (set) originally declared in a superclass. This enables the subclass to intercept assignment operations, allowing for the execution of custom logic, validation, or state management before, after, or instead of the superclass’s implementation.

Syntax and Implementation

To override a setter, the subclass must define a set accessor matching the name of the property in the superclass. The @override annotation enforces compile-time verification that the member exists in the superclass. When overriding an explicit setter (one defined with the set keyword in the parent), the subclass implementation replaces the parent’s method body.
class SuperClass {
  double _speed = 0;

  // Explicit setter declaration
  set speed(double value) {
    _speed = value;
  }
}

class SubClass extends SuperClass {
  @override
  set speed(double value) {
    // Intercepts the assignment
    print('Intercepting speed assignment: $value');
    
    // Delegates to the superclass to update the private field `_speed`
    super.speed = value; 
  }
}

Overriding Implicit Setters (Fields)

In Dart, a non-final instance variable (field) implicitly generates both a getter and a setter. To override the setter logic of a field declared in a superclass, the subclass must explicitly override both the getter and the setter. Overriding only the setter is a compile-time error, as it would result in an incomplete property definition in the subclass. The subclass typically uses super to delegate storage and retrieval to the superclass’s field.
class Container {
  // Implicitly generates 'int get capacity' and 'void set capacity(int value)'
  int capacity = 10; 
}

class SecureContainer extends Container {
  // Must override the getter to maintain read access via super
  @override
  int get capacity => super.capacity;

  // Overrides the implicit setter with explicit logic
  @override
  set capacity(int value) {
    // Custom logic (e.g., validation or logging)
    if (value < 0) throw ArgumentError('Capacity cannot be negative');
    
    // Forward the value to the superclass field for storage
    super.capacity = value;
  }
}

Type Constraints and Covariance

By default, the parameter type of an overridden setter must be the same as, or a supertype of, the parameter type defined in the superclass. This adheres to standard contravariance rules for function parameters. To narrow the parameter type (accept a subtype) in the subclass setter, the covariant keyword is required. This disables static type safety checks for that specific parameter, deferring type verification to runtime.
class Animal {}
class Dog extends Animal {}

class Trainer {
  set trainee(Animal a) {}
}

class StrictDogTrainer extends Trainer {
  // The 'covariant' keyword allows narrowing 'Animal' to 'Dog'
  @override
  set trainee(covariant Dog d) {
    super.trainee = d;
  }
}

Execution Flow and State Management

  1. Invocation: When an assignment is made to the property on a subclass instance, the runtime invokes the subclass’s set implementation.
  2. Interception: The code block within the subclass setter executes.
  3. Delegation: If the subclass does not maintain its own state for the property, it must invoke super.propertyName = value.
    • If the superclass defined an explicit setter, this calls that method.
    • If the superclass defined a field, this writes directly to that storage slot.
    • Omitting the super call prevents the value from propagating to the parent class, potentially leaving the object state unchanged regarding that property.
Master Dart with Deep Grasping Methodology!Learn More