简体   繁体   中英

Dart class constructor initialise fields in constructor body

I am trying to initialise the field tasks in the below code but I am getting the error: Non-nullable instance field 'tasks' must be initialized. . I can successfully initialise fields using syntax like Example(this.tasks) {} or Example(String json): this.tasks = [json] but I am unsure how to initialise a field when I need to use multiple lines to calculate the value like in the below code.

import 'dart:convert';

class Example {
  List<String> tasks;
  Example(String json) {
    List<String> data = jsonDecode(json);
    this.tasks = data;
  }
}

In this example you don't need multiple lines to compute the value. You can just do:

Example(String json) : this.tasks = jsonDecode(json);

In the more general case where you do need multiple statements, if the field initialization values are unrelated, I'd use helper functions for each:

Example(String json) : this.tasks = _computeTasks(json);
static List<String> _computeTasks(String json) {
  List<String> result;
  // compute compute compute
  return result;
}

If you have multiple fields that need to be initialized with values from the same computation, I'd first try to make it a factory constructor:

  final Something somethingElse;
  Example._(this.tasks, this.somethingElse);
  factory Example(String json) {
    List<String> tasks;
    Something somethingElse;
    // compute compute compute
    return Example._(tasks, somethingElse);
  }

If the constructor needs to be generative, and you need to compute multiple values in the same computation, and it's really important to not change this, then I'd probably make an intermediate object holding those values:

  Example(String json) : this._(_computeValues(json));
  Example._(_Intermediate values) 
      : tasks = values.tasks, somethingElse = values.somethingElse;
  static _Intermediate _computeValues(String json) {
    List<String> tasks;
    Something somethingElse;
    // compute compute compute
    return _Intermediate(tasks, somethingElse);
  }
  ...
}

// Helper class.
class _Intermediate {
  final List<String> tasks;
  final Something somethingElse;
  _Intermediate(this.tasks, this.somethingElse);
}

If the types are the same, you can perhaps use a List instead of a helper class for the intermediate value. Or, you might be able to reuse a class like

class Pair<S, T> { 
  final S first; 
  final T second; 
  Pair(this.first, this.second);
}

It's not very important how you do it, the user will never see the intermediate value.

You are correct, non-nullable values must be initialized first upon object construction either as arguments, or in the initializer list.

You cannot however call methods on your object before initialization is complete. (And you probably shouldn't anyway as object construction should be kept as lightweight as possible)

If you have any processing that needs to be done before (or after) constructing an object, a factory constructor can be used. In fact it looks like you are trying to create an object from json which is just what is exemplified in the official docs .

To simplify that example i have linked to and remove anything about caching, it would look something like this:

class Logger {
  String name;
  bool mute = false;

  factory Logger.fromJson(Map<String, Object> json) {
    return Logger._internal(json['name'].toString());
  }

  Logger._internal(this.name);

}

Irn's solution is good. You could also make tasks nullable, if that's OK with you:

  List<String>? tasks;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM