4

Given the following code:

const jsonString = '{"myString":"Hello"}';
final jsonMap = jsonDecode(jsonString);

final myObject = MyClass.fromJson(jsonMap);

How many ways are there to create a new object using this syntax:

MyClass.fromJson(jsonMap)

Recently I've been trying to understand the differences between named constructors, factory constructors and static methods so I'm posting my answer below so that I have something to come back to as a reference in the future.

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393

2 Answers2

5

To create a new instance of an object using the following syntax:

MyClass.fromJson(jsonMap)

For use with the following code:

// import 'dart:convert';

const jsonString = '{"myString":"Hello"}';
final jsonMap = jsonDecode(jsonString);

final myObject = MyClass.fromJson(jsonMap);

There are at least the following ways to do it (with supplemental notes about the characteristics of each):

Generative constructor

class MyClass {
  MyClass(this.myString);
  final String myString;

  MyClass.fromJson(Map<String, dynamic> json) : this(json['myString']);
}

There are two kinds of generative constructors: named and unnamed. The MyClass.fromJson() is a named constructor while MyClass() is an unnamed constructor. The following principles apply to generative constructors:

  • Generative constructors may only instantiate the class itself.
  • Generative constructors can use an initializer list.
  • Generative constructors may only use initializing parameters or the initializer list to set final properties, that is, not in the constructor body.
  • Generative constructors can be const, even if they are not redirecting.

Factory constructor

class MyClass {
  MyClass(this.myString);
  final String myString;

  factory MyClass.fromJson(Map<String, dynamic> json) {
    return MyClass(json['myString']);
  }
}
  • Factory constructors may return a subtype of the class.
  • Factory constructors can be used to create singletons.
  • Factory constructors can be unnamed like generative constructors.
  • Factory constructors can be const, but only when redirecting.

Static method

class MyClass {
  MyClass(this.myString);
  final String myString;

  static MyClass fromJson(Map<String, dynamic> json) {
    return MyClass(json['myString']);
  }
}
  • Static methods may return anything, including a Future.
  • Static methods can be used to create singletons.
  • Static methods can be used as tear-offs.

Further reading

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • What do you mean with `Named constructors may only use the initializer list to set final properties.`? even with named constructors like `MyClass.named(this.myString)` you can initialize final properties. Also you can create singletons using a `factory` constructor like [here](https://gist.github.com/Hexer10/027b709526fdb77164186ee3cd884e99) – Mattia Aug 07 '20 at 08:30
  • @Mattia, thanks for pointing that out. My meaning was that final properties can't be set in the named constructor body, but I wasn't specific enough. I updated my answer for that one. Regarding singletons, I mentioned that one already. (However, having it under static methods also might make it look like that is the only way to create a singleton. Is that true?) – Suragch Aug 07 '20 at 09:53
  • @lrn I was under the impression that generative constructors were the ones without names, that is, the ones that simply use the class names. Is that not true? – Suragch Aug 08 '20 at 07:30
  • @lrn I asked my question in more detail here: https://stackoverflow.com/questions/63313102/are-named-constructors-a-subset-of-generative-constructors-in-dart – Suragch Aug 08 '20 at 08:04
  • Answered there, but no, its not true. Being generative or factory is orthogonal to being named or not. Factory constructors are those prefixed by `factory`, the rest are generative. Either can be named or unnamed. – lrn Aug 08 '20 at 23:29
1

In addition to @suragch detailed answer. I like to give some bullet points that show factory constructor is the best option for the above scenario (for fromJson() method).

  • When using factory constructors, you don't need to initialize instance variables of that class. (but when you using generative constructors, need to initialize all the final instance variables)

  • Factory constructor can return an existing object. Eg:- when using json_seriazible package, fromJson() method return an existing (previously made) object. So we can only use factory constructors with this package.

  • Factory constructors can return any subtype of that class, but when using generative constructors, it can only return the exact type object of that class.

  • Ensures only one instance of a class is ever created (singleton pattern). (object are expensive, so singleton pattern should needed for fromJson)

According to the above points, we can see generative constructors add more limitations for fromJson constructor and static methods give fewer limitations for fromJson so it can cause type errors by returning different type objects.

dilshan
  • 2,045
  • 20
  • 18
  • For your point 4, I would qualify that by saying it _can_ be used to create a singleton. However, a factory constructor can also be used to create a new object every time. – Suragch Apr 21 '21 at 09:17
  • @Suragch [If factory constructor returns an existing object => singleton pattern True(point 4 is correct.)] [But if factory constructor returns constructor calling(create a new instance) => singleton pattern False(point 4 is incorrect.) I think this is the point you try to say. Am I correct? – dilshan Apr 21 '21 at 11:56