dart advantage of a factory constructor identifier - dart

I've been investigating JSON parsing for my Flutter app and have a question about factory constructors that I can't resolve. I'm trying to understand the advantage of using a factory constructor versus a plain constructor. For example, I see quite a few JSON parsing examples that create a model class with a JSON constructor like this:
class Student{
String studentId;
String studentName;
int studentScores;
Student({
this.studentId,
this.studentName,
this.studentScores
});
factory Student.fromJson(Map<String, dynamic> parsedJson){
return Student(
studentId: parsedJson['id'],
studentName : parsedJson['name'],
studentScores : parsedJson ['score']
);
}
}
I've also seen an equal number of examples that DON'T declare the constructor as a factory. Both types of classname.fromJSON constructors create an object from the JSON data so is there an advantage to declaring the constructor as a factory or is using a factory here superfluous?

A normal constructor always returns a new instance of the current class (except when the constructor throws an exception).
A factory constructor is quite similar to a static method with the differences that it
can only return an instance of the current class or one of its subclasses
can be invoked with new but that is now less relevant since new became optional.
has no initializer list (no : super())
So a factory constructor can be used
to create instances of subclasses (for example depending on the passed parameter
to return a cached instance instead of a new one
to prepare calculated values to forward them as parameters to a normal constructor so that final fields can be initialized with them. This is often used to work around limitations of what can be done in an initializer list of a normal constructor (like error handling).
In your example this code
studentId: parsedJson['id'],
studentName : parsedJson['name'],
studentScores : parsedJson ['score']
could be moved to the body of a normal constructor because no final fields need to be initialized.

In the particular example in the question, there's no advantage to using a factory constructor. It makes no difference to callers (there is no expectation to receive an already-existing object), and this particular factory constructor could have been a normal constructor that delegated to the main constructor instead.
In general, the factory keyword is not very useful and provides an advantage only in special circumstances.
A factory constructor vs. a normal constructor
A factory constructor invokes another constructor.
Since a factory constructor does not directly create a new instance, it cannot use a constructor initializer list.
A normal constructor always returns a new instance of the class. A factory constructor is permitted to return an existing instance, an instance of a derived class, or null. (However, some people dislike returning null from a factory constructor. Note that returning null from a factory constructor is disallowed with null-safety.)
Due to the above, an extending class cannot invoke a factory constructor as the superclass constructor. A class that provides only factory constructors therefore cannot be extended with derived classes.
A factory constructor vs. a static method
A factory constructor can be the unnamed, default constructor of a class.
A factory constructor can be used with new. (But using new is now discouraged.)
Until Dart 2.15, constructors could not be used as tear-offs (i.e., they could not be used as callbacks), whereas static methods could.
Static methods can be async. (A factory constructor must return a type of its class, so it cannot return a Future.)
Factory constructors can be declared const.
In null-safe Dart, a factory constructor cannot return a nullable type.
In generated dartdoc documentation, a factory constructor obviously will be listed in the "Constructors" section (which is prominently at the top) whereas a static method will be in the "Static Methods" section (which currently is buried at the bottom).

After I've been noticing and wondering the same, and given I don't think the other answers actually answer the question ("I've been investigating JSON parsing [...] I'm trying to understand the advantage of using a factory constructor verses a plain constructor"), here my try:
there's no advantage or difference that I could see or understand, when parsing json, in using a factory constructor instead of a plain constructor. I tried both, and both works fine, with all the types of parameters. I decided eventually to adopt the factory constructor, because of the convenience of how the code is written, and readability, but it's a matter of choice and both will work fine in all the cases.

One of the uses of factory constructor is, we can decide which instance to create, at run-time and move all the logic to the parent's factory constructor
let's say you have 1 parent class and 2 subclasses
class GolderRetriever extends Dog{
GolderRetriever(String name):super(name);
}
class Labrador extends Dog{
Labrador(String name):super(name);
}
Then we have the parent class
class Dog{
final String name;
Dog(this.name);
factory Dog.createInstance({required String name,DogType type=DogType.UNKNOWN}){
if(type==DogType.GOLDEN_RETRIEVER){
return GolderRetriever(name);
}
else if(type==DogType.DALMATION){
return Labrador(name);
}else{
return Dog(name);
}
}
}
and also I have enum DogType
enum DogType{
GOLDEN_RETRIEVER,DALMATION,UNKNOWN
}
Then in the main Method, you just delegate which subclass instance you want to create to the parent Dog class
main() {
Dog myDog = Dog.createInstance(name:"Rocky",type:DogType.DALMATION);
Dog myNeighboursDog = Dog.createInstance(name:"Tommy",type:DogType.GOLDEN_RETRIEVER);
Dog strayDog = Dog.createInstance(name:"jimmy");
}
you can't do this with a named constructor as you can create only the instance of that class(Dog class), not its subtypes.
Now the responsibility of which instance to create is delegated to the parent class. This can remove a lot of if-else boilerplate code. When you want to change the logic, you just change that in Animal class alone.

Related

Why factory constructor can't be used in invoking superclass?

Minimal reproducible code:
class Foo {
Foo();
factory Foo.named() => Foo();
}
class Bar extends Foo {
Bar() : super.named(); // Error
}
As there's no async-await thing in the factory constructor, and it instantly invokes the concrete constructor, so why isn't it allowed to use factory constructor like that?
Before null safety, null could be returned from a factory constructor but it is no longer a case. So, I think a factory constructor should now be allowed.
factory function can return subclass, which means can call subclass's init function.
if subclass self can call super.factory function, then it become cycling.
Calling a generative constructor creates a new object, then runs the constructor and super-class constructors to initialize that new object.
They are initializers which initialize the object created by the (now often implicit) new operator.
The super-constructor chained by the (non-redirecting) generative constructor is called to initialize the same object, and ensure that its fields are fully initialized, before ever giving anyone access to that object.
Calling a factory constructor does not create any new object, it just executed the body of that constructor which can do anything that normal code can. (That may or may not include calling a generative constructor to create an object. It can also just throw and never create any object.) A factory constructor cannot access this, because there is no "current object" before it returns one.
Let's assume that Foo has an int foo = 42; field. A factory constructor like the Foo.named here creates a new object by calling Foo() - the generative constructor.
When you do new Bar(), you create a new Bar object and ask the super-constructors to initialize that object. The named factory constructor has no access to that new object that would be created by calling new Bar(), it cannot access this and it cannot help initialize the foo field of that object. It creates a new, unrelated, Foo object and now you have 1) a partially initialized Bar object and 2) a Foo object, neither of which can be the result of calling a Bar constructor.
Your generative constructors must call a super-class generative constructor, which does the same until reaching the Object constructor. That's the only way to ensure that the newly generated object is fully initialized.
One of the main purposes of a factory constructor is to allow returning an existing instance. That's basically incompatible with a derived class constructor that must create a new object. (I suppose it doesn't necessarily have to be; you could imagine a design where two derived instances virtually inherit from a shared instance of a base class. However, that would be a lot of extra work for something of little benefit and that could be very confusing.)
Additionally, one of the other main purposes of a factory constructor is precisely to prevent a class from being subclassed by making all non-factory constructors private.

Why must the _internal() be called in the class?

Even though I am familiar with singleton, Dart's factory is confusing. I have seen an existing question, but the only thing I got from the answer is that the name can be whatever identifier that starts with an _. Even though the person had asked why it should be called, the answer does not seem to be explaining that.
If I comment out the Dog._internal(); line, I get Method not found: 'Dog._internal'. It looks like calling a method, but is it like C's function prototype? But it has no return type. Can someone explain the Dog._internal(); line to me?
class Dog
{
static final Dog dog = Dog._internal();
//Dog._internal();
factory Dog()
{
return dog;
}
}
There are multiple concepts going on so lets go through the example:
factory Dog()
{
return dog;
}
This defines a factory constructor. Factory constructors are much like normal static methods but must always return an object which are compatible with the type of which the factory constructor is part of. In our example the factory constructor must return a Dog object.
It is not a constructor in that sense that we already have created a object when we enter this method. Again, it can be compared to static Dog Dog() but is allowed to override the default constructor. So we must create the object manually and return it.
This allows us to control the behavior of when objects are created and in our case allows us to have a singleton pattern so when somebody are trying to create an instance of Dog they will always get the same object.
Dog._internal();
This is called a named constructor. Constructors in Dart can have given names which makes it easier to distinguish different constructors. But also because Dart does not allows us to override methods with different number of parameters.
Also, because of the name starting with _ it means this constructor is marked private and cannot be used outside of the library it is part of. So if your code is part of a library, the code importing your library are not allowed to call your private constructor.
This is again a nifty trick to allow us to make a singleton since we don't want other than our own code to create a new instance (which are the only instance we want to create).
static final Dog dog = Dog._internal();
This is essential creating the singleton. The reason for this is that static variables in Dart are lazy-evaluated so the value dog does not really have any value before it is called. After first call, the value are "cached" so Dog._internal(7) are only called once as long our application (or more specific, our isoleate instance) are running.
I would properly call the variable _instance or _dog so it is also private.
Dog.foo(); defines a named constructor named foo that takes no arguments and has no body. It's equivalent to Dog.foo() {}. Similarly, Dog._internal(); defines a named constructor named _internal. This would be more obvious if the constructor took arguments and did work, for example:
class Dog {
static final Dog dog = Dog._internal(7);
Dog._internal(this.years) {
registerAnimal(this);
}
factory Dog() {
return dog;
}
int years;
}

What advantages of factory constructor over standalone factory class or function?

I've just started learning Dart and I wonder what advantages of factory constructor over standalone factory class or function?
When we add new derived class then we need to change factory method inside of our abstract class, it is fine if we have its code, but if it comes with library - factory constructor becomes useless?
Or there is some mechanism to update factory method with info about derived classes? If it so, please share an example.
factory lets you return subtypes, which is very useful.
For instance, you could have factory MyClass.empty => const _EmpytMyClass() and return a trivial implementation with no storage.
You can also use it to returned cached values if you want to have canonical instances of your class.
You can also use factory to call methods to create your instances. See here: https://github.com/dart-lang/json_serializable/blob/4033f6ad4e3c96bc2ed16d93c0995e11fcfe42df/example/lib/example.dart#L29

Can I get an InstanceMirror of a class without using any constructor in Dart?

Can I get an InstanceMirror without using any constructor in Dart?
More precisely: I have a class with or without any special constructor, yet I'd like to get an InstanceMirror without having to provide a constructor or any arguments at all.
For example PHP offers ReflectionClass::newInstanceWithoutConstructor
Cheers
Why do you need an InstanceMirror and why can't you use a ClassMirror?
Just list all the available constructors from the ClassMirror and then use a constructor with 0 arguments to create a new instance.
The PHP Version says: "Creates a new class instance without invoking the constructor.". For me this totally makes no sense. That's why there are constructors: To be called at creation time.
New
If the class doesn't have a constructor, a default constructor with no arguments is implicitly created for this class and you can use it.
If a class has one or more explicit constructors, you can create a new instance only by using one of them.
Old
I'm not sure if I fully understand your questions, but basically - no. If your class doesn't have a constructor an implicit default constructor is used.
An instance of a class is created with new SomeClass which creates a new instance and calls the constructor. There are other ways like literals {'x': 1, 'y': 2} but I'm pretty sure this way a constructor is called as well.

How do I guarantee a certain named constructor in Dart?

Let us say I have the concrete classes Cat, Dog, and Parrot, and the following interface:
class HasGuid {
HasGuid.fromId(String id);
}
My goal is to guarantee that Cat, Dog, and Parrot all have a fromId named constructor. So, I can make calls like:
Cat.fromId("Whiskers") =returns> [A Future<Cat> object with id "Whiskers"]
Dog.fromId("Fido") =returns> [A Future<Dog> object with id "Fido"]
Parrot.fromId("Polly") =returns> [A Future<Parrot> object with id "Poly"]
fromId is making a call across the network, for this reason I return it as a Future. I basically want a contract that states that any class that mixes/extends/implements/whatever the HasGuid class will have a named constructor of fromId. Where fromId on class T will take an identity string and will return a Future<T>.
The short answer is that you cannot force a subclass to implement a specific named constructor ... all you can do is force the subclass to make sure the named constructor is called by the subclass.
Take for example the following ...
class Animal {
String name;
Animal.fromName(this.name);
}
class Dog extends Animal {
Dog.withName(String name) : super.fromName(name);
}
Note the following ...
Animal has no zero argument constructor.
If you don't call the super.fromName() constructor from Dog you will get a compile error
The superclass 'Animal' doesn't have a zero argument constructor.
Although Dog must call the fromName() constructor ... it doesn't have to call its named constructor the same. In this case notice it is called withName().
There can be no assurance on constructor.
Interface(implements) assurance on instance methods.
Super class(extends) or Mixins(with) also assurance on instance methods, not constructor.
Constructor return it's own type, not a Future.
So they should all have a static method or class, but no guarantee.

Resources