Let's say you're managing the list of serial numbers of the bicycles owned by residents in your building, with the objective of planning ahead to build additional safe bike storage.
Some people will of course have no bikes.
In Dart > 2.12 (with null safety) you could
use a non-nullable List<String>
and initialize it to an empty list
class Resident {
String name;
List<String> bicycles = [];
}
or you could use a nullable List<String>
and use null
as a flag to signal that someone has no bikes.
class Resident {
String name;
List<String>? bicycles;
}
Both designs are of course workable, but does it turn out down the road that one is clearly better than the other—more idiomatic to the new Dart, for example? In other words, what's better, an initially empty non-nullable or an initially nullable and null container?
Even if I count the bits needed, it's not quite clear. There would be wasted storage to construct an empty list in the first case, but there is also storage wasted in the second case—though it's of an unknown, and perhaps implementation dependent—amount.
CodePudding user response:
If you want to represent having none of something, then prefer non-nullable container types with an empty state to nullable types.
- With a nullable container, you need to potentially need to deal with the container being empty anyway, and now you have to do extra work to check for
null
everywhere. Meanwhile, dealing with an empty container often doesn't involve any extra work. Contrast:
with// Nullable case. final bicycles = resident.bicycles; if (bicycles != null) { for (var bicycle in bicycles) { doSomething(bicycle); } }
/// Non-nullable case. for (var bicycle in resident.bicycles) { doSomething(bicycle); }
- You could try to reset references to
null
when the container becomes empty so that there aren't two cases to deal with, but as noted above, the empty case often is free anyway, so that'd be more work for usually no gain. Furthermore, resetting references can be a lot of extra work:
Ifvar list = [1, 2, 3]; mutateList(list); if (list.isEmpty) { list = null; }
mutateList
could remove elements fromlist
, then every caller would need to do extra work to replace emptyList
s tonull
. - Even if you don't care to replace empty containers with
null
, you'd still have different behaviors when transitioning to a non-empty container. Consider:
What would you expectvar sam = Resident(); var samsBicycles = sam.bicycles; sam.addBicycle();
samsBicycles
to be? IfResident.bicycles
is initiallynull
, thensamsBicycles
will remainnull
and will no longer refer to the same object assam.bicycles
.