site-www icon indicating copy to clipboard operation
site-www copied to clipboard

Improve setters and getters examples on 'Methods' page

Open dtonhofer opened this issue 2 months ago • 0 comments

Page URL

https://dart.dev/language/methods/

Page source

https://github.com/dart-lang/site-www/tree/main/src/content/language/methods.md

Describe the problem

At

https://dart.dev/language/methods#getters-and-setters

we read about getters and setters but the text feels much too short and the example could be extended (the example code also does not really tell a good story, IMHO).

Suggesting more examples to illustrate various setter/getter "do's" and "don'ts" (including one involving a final field that creates a recursive call, see below) that tell a more structured story. Especially for people coming from Java (like me) this is helpful.

Note on terms: I am using "synthetic field" for a setter/getter that simulates the presence of field, not sure whether this is the correct term.

Here we go:

import 'dart:math';

// A class with some fields.

class Example {

  int a;

  int _b;
  final int c;
  static int d = 1000;

  Example(this.a,this._b,this.c);

}

// You will get default getters and setters on class "Example":

void handleExample() {
  final ex = Example(1,2,3);
  // There will be a setter and getter for field "a".
  final v = ex.a;
  ex.a = 10;
  ex.a++;
  assert(ex.a == 11);
  // There will be a setter and getter for field "_b".
  // But both are not visible outside of the library (this file)
  final u = ex._b;
  ex._b = 20;
  assert(ex._b == 20);
  // There will be a getter and getter for field "c", but no setter.
  final w = ex.c;
  // ex.c = 10; // NO! "'c' cannot be used as a setter"
  // There will be a static getter and getter for static field "d".
  final z = Example.d;
  Example.d = 10;
  Example.d++;
  assert(Example.d == 11);
}

// You cannot override a getter or setter with a method.
// Moreover, the actual setter name for field "a" is really "a="
// instead of "a". "a=" is not a valid name for a manually written
// method, so you cannot even name the setter (that's how I understand
// it at least.)

class ExampleFaulty extends Example {

  // Both
  // ExampleFaulty(super.a, super.b, super.c);
  // and
  // ExampleFaulty(super.a, super._b, super.c);
  // work here.

  ExampleFaulty(super.a, super.b, super.c);

  // This will NOT compile:

  int a() {
    return a;
  }

  // This will NOT compile:

  void a(int x) {
    a = x;
  }

}

// You can define Java-style getX() and setX() methods.
// They are just common methods, nothing special.

class ExampleJavaStyleGetterSetter extends Example {

  ExampleJavaStyleGetterSetter(super.a, super.b, super.c);

  int getA() {
    return a;
  }

  void setA(int x) {
    a = x;
  }
}

// You can override the default setter and getter of a field
// in a subclass. There is a function syntax and a standard syntax
// to do that.

class ExampleOverrideGetterSetterOfA1 extends Example {

  ExampleOverrideGetterSetterOfA1(super.a, super.b, super.c);

  set a(int x) => a = x;
  int get a => a;
  
}

class ExampleOverrideAccessorsOfA2 extends Example {

  ExampleOverrideAccessorsOfA2(super.a, super.b, super.c);

  set a(int x) {
    print("Setter for 'a' has been called");
    a = x;
  }

  int get a {
    print("Getter for 'a' has been called");
    return a;
  }

}

// You CANNOT override the implicit setter and getter of a non-final field.
// in the same class (as opposed to in a superclass.) You will get a
// "duplicate_definition" error ("The name 'a' is already defined")

class ExampleFaultyOverridingGetterSetter {

  int a = 1;

  // Both of these will NOT compile:
  
  set a(int x) => a = x;

  int get a => a;

  // Both of these will NOT compile either:

  set a(int x) {
    a = x;
  }

  int get a {
    return a;
  }
}

// You SEEMINGLY can add a setter for a final field!
// But this is actually just an endless recursive call
// that will lead to a stack overflow.

class ExampleOverridingSetterOnFinalField {

  final int a = 1;

  // DON'T DO THIS, even if it compiles!

  set a(int x) => a = x;

}

// You can have a setter and getter for a "synthetic field",
// here the "opposite" of "angle", rotated by Pi (180°)

class ExampleSyntheticField {

  double angle = 0.0;

  static double _canonicalize(double x) {
    if (x >= 0.0) {
      return x.remainder(2.0*pi);
    }
    else {
      return 2*pi + x.remainder(2.0*pi);
    }
  }

  double get opposite => _canonicalize(angle + pi);
  set opposite(double x) => angle = _canonicalize(x - pi);

}

// The following output is produced by the example code:
// The opposite of 0.5 π is 1.5 π
// If the opposite is 0.5 π, the angle is 1.5 π
// If the opposite is 0.7499999999999997 π, the angle is 1.75 π

void handleExampleSyntheticField() {
  final ExampleSyntheticField obj = ExampleSyntheticField();
  obj.angle = 0.5 * pi;
  print("The opposite of ${obj.angle/pi} π is ${obj.opposite/pi} π");
  obj.opposite = 0.5 * pi;
  print("If the opposite is ${obj.opposite/pi} π, the angle is ${obj.angle/pi} π");
  obj.opposite += 0.25 * pi;
  print("If the opposite is ${obj.opposite/pi} π, the angle is ${obj.angle/pi} π");
}

void main() {
  handleExample();
}

Expected fix

No response

Additional context

No response

I would like to fix this problem.

  • [ ] I will try and fix this problem on dart.dev.

dtonhofer avatar Apr 29 '24 16:04 dtonhofer