0
votes

I have a question related to Dart Null Safety concept.

Imagine I have a class called Bird

class Bird{
Object character;

Bird();
}

and Pigeon

class Pigeon extends Bird{
String name;

Pigeon();
}

Now, because of Null Safety on Dart, the character object must be instantiated. I want to instantiate that in the constructor because I want the bird class can be testable using Mockito.

But, when I wrote this

class Bird{
late Object character;

Bird(this.character);
}

The Pigeon class shows an error because the Bird class doesn't have any zero-argument constructor.

My workaround is by using a not required argument like this.

class Bird{
late Object character;

Bird({Object? character}) : this.character= character ?? GetIt.I<Object>();;
}

But, I don't like the GetIt part for my default value. Moreover, another programmer may think it is an optional argument because I didn't mark it as required which can lead to an error.

So, how is the best practice to solve this case?

3
Exactly what do you want character to be for a Pigeon instance? Why don't you have Pigeon pass an appropriate argument to the Bird constructor? You also seem to be misunderstanding some other things (e.g., you don't need to use late, and required is necessary for named arguments to distinguish required from optional named arguments). - jamesdlin
Don't use late. It is very bad idea. Try to find a better solution. - mezoni
@jamesdlin the character object is just an example. It can contain bird basic characteristic (e.g. type of the bird's beak). Furthermore, in a non-null variable, the "required" keyword is necessary if you declared that in the constructor as a namedArgs. - Darari Nur Amali
@mezoni agreed. Have any idea? That's is why I want a discussion here. - Darari Nur Amali
@DarariNurAmali Anyway, my point is that we can't give a good answer about how to solve your problem, because without knowing what you want character to be for Pigeon, we can't really tell how you should initialize it. If it should have the same initial value for all Pigeon instances, then the Pigeon constructor should just call the Bird constructor with an appropriate argument. Or if it's something that varies across Pigeons, then it perhaps should be an argument to Pigeon's constructor too. - jamesdlin

3 Answers

0
votes

You can either use Named Argument Constructor to make character optional or remove late and ? like this

class Bird{
  Object character;

  Bird(Object character) : this.character = character ?? GetIt.I<Object>();
}

Your Named Argument Constructor will look like this

class Bird{
  Object character;

  Bird({this.character = DEFAULT_CONST_VALUE});
}
0
votes

What about this solution?

class Bird {
  Object character;

  Bird({this.character});
}

class Pigeon extends Bird {
  String name;

  Pigeon({this.name, Object character}) : super(character: character);
}

Or even this?

class Bird<T> {
  T character;

  Bird({this.character});
}

class Pigeon extends Bird<String> {
  String name;

  Pigeon({this.name, String character}) : super(character: character);
}

Or this?

class Character {
  //
}

class Bird<T extends Character> {
  T character;

  Bird({this.character});
}

class Pigeon extends Bird<PigeonCharacter> {
  String name;

  Pigeon({this.name, PigeonCharacter character}) : super(character: character);
}

class PigeonCharacter extends Character {
  //
}
0
votes

You need to use the named constructor as below

class Bird{
 Object character;
  
 Bird({this.character});
  
 Bird withCharater(Object character) {
   return Bird(character: character);
 }
}

class Pigeon extends Bird{
 String name;

 Pigeon(); 
}