6
votes

I have a Dart class with multiple fields that have to be final, because the class extends another class marked with @immutable. The values of these fields are supposed to be calculated when an instance of the class is created. In Dart, "final instance variables must be initialized before the constructor body starts" (from dartlang.org). In that scope, you can only call static methods.

That works for me except some fields depend on the same calculation, meaning that the same calculation is done twice. Is there any way to avoid that, i.e. by saving some temporary result?

My current code:

class _IntegralCurve extends Curve {
  static double delta = 0.01;

  _IntegralCurve(this.original) :
      integral = calculateIntegral(original),
      values = calculateNormalizedValues(original);

  final Curve original;
  final double integral; // Accessible to other classes.
  final Map<double, double> values;

  /// Does the actual integrating work. Called twice.
  static Map<double, double> integrate(Curve original) {
    double integral = 0.0;
    final values = Map<double, double>();

    for (double t = 0.0; t <= 1.0; t += delta) {
      integral += original.transform(t) * delta;
      values[t] = integral;
    }
    values[1.0] = integral;
    return values;
  }

  /// Calculates the integral.
  static double calculateIntegral(Curve curve) => integrate(curve)[1];

  /// Calculates cumulative values.
  static Map<double, double> calculateNormalizedValues(Curve curve) {
    final values = integrate(curve);

    for (final double t in values.keys) {
      values[t] = values[t] / values[1];
    }
    return values;
  }

  double transform(double t) {
    for (final key in values.keys)
      if (key > t)
        return values[key];
    return values[1.0];
  }
}
3

3 Answers

10
votes

Calculate the values in a factory constructor:

class _IntegralCurve extends Curve {
  static double delta = 0.01;

  factory _IntegralCurve(Curve original) {
    final integral = calculateIntegral(original),
    final values = calculateNormalizedValues(original);
    return _IntegralCourve._(original, integral, values);
  }

  _IntegralCurve._(this.original, this.integral, this.values);
4
votes

You can use a factory to hide calculations within a constructor without loosing the final:

class Foo {
  final int computed;
  final int copy;

  Foo._(this.computed, this.copy);

  factory Foo() {
    // calculate value
    int value = 42;
    return Foo._(value, value);
  }
}
0
votes

If you only want this extra value to be looked up by other classes you could use the get keyword to compute other fields. This value will be computed every time it gets called though.

// Declaration
class Foo {
  const Foo(this.initialValue);

  final double initialValue;

  double get computedValue => initialValue + initialValue;
}

// Usage
main() {
  final foo = Foo(20);
  print("${foo.initialValue} ${foo.computedValue}"); // Would print `20 40`
}

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