dartinheritancesetter

how to override a setter of an abstract class to make the value final


Given an abstract class "Foo" that has a getter and setter for a "value"

abstract class Foo<T> {
  T get value;
  set value(T newValue);
}

I'm trying to override getter and setter to make the value final;
with the implementation below

class Bar implements Foo<String> {
  @override
  final String value;
  const Bar(this.value);
}

the linter complains Missing concrete implementation of 'setter Foo.value'.,
but if we add the implementation

class Bar implements Foo<String> {
  @override
  final String value;
  const Bar(this.value);
  @override
  set value(String newValue) => value = newValue;
}

when we use the setter

void main() {
  const bar = Bar('hello');
  bar.value = 'world';
  print(bar.value);
}

we don't receive ant warning about using the setter for a final value, but at runtime we incur in this error

Uncaught RangeError: Maximum call stack size exceededError: RangeError: Maximum call stack size exceeded

the problem seems to be only for the setter, therefor\

what is correct way to override a setter of an abstract class to make the value final?


Solution

  • There is no correct way to disable a setter in the base class because doing so is fundamentally incorrect. A derived class must be substitutable for the base class. If the derived class did not provide a setter, then it would no longer be providing the base class's interface and would violate the substitution principle. Consider:

    void updateFoo<T>(Foo<T> foo, T newValue) => foo.value = newValue;
    
    void main() {
      var bar = Bar('hello');
      updateFoo(bar, 'world');
    }
    

    It is not possible to issue any compile-time warnings or errors; the implementation and call of updateFoo are both legal from static analysis.

    Instead consider:

    Also, your specific exception occurs because of a different mistake in your implementation. When you do:

      @override
      set value(String newValue) => value = newValue;
    

    then the value = newValue; statement will trigger the value setter again, resulting in infinite recursion as hinted at by the error message. If you instead tried to do:

      final String _value;
    
      @override
      String get value => _value;
    
      @override
      set value(String newValue) => _value = newValue;
    

    then your Bar implementation would have generated a compile-time error for trying to reassign _value, which is final. And if you made _value non-final, then the compiler would have generated a compile-time error telling you that Bar then could not have a const constructor.