Not null-able
If we annotate a type to be say, int, it means it can only be an integer. It cannot be null.
1 |
int a = null // Error, a can't be null |
We can’t add two numbers if one of them is null:
1 2 3 4 |
int a; // null int b = 2; print(a + b); // Without Nullable, we get run time error! |
Nullable (?)
If you want that int to be null, use the question mark to denote that.
1 |
int? a = null; // Can be null |
1 2 3 |
int? a; // null int b = 2; print(a+b); // <-- compile time error here! |
With nullable, we get Compile Time Error.
This makes us write safer programs.
Assertion Operator (!)
If you’re sure that a nullable variable will ALWAYS have non-nullable values,
it’s safe to assign it to a non-nullable variable with the ! operator.
1 2 |
int? aNullableInt = 8; print(aNullableInt!.isEven); // 8 |
However, if you’re wrong, you’ll get a runtime error!
1 2 3 4 5 |
int? aNullableInt = null; print(aNullableInt!.isEven); // run time error! // Unhandled exception: // Null check operator used on a null value |
if-null operator (??)
In the example, x is -1, and thus maybeValue will be null;
1 2 3 4 5 6 |
int x = -1; int? maybeValue; // null, due to x is -1 if ( x > 0) { print('larger than 0'); maybeValue = x; } |
Usually, we’d write it like this:
1 |
int value = maybeValue == null ? 0 : maybeValue; |
Now, we can use if-null operator like so:
1 2 3 |
// if not null, assign value to maybeValue // if null, assign 0 to value int value = maybeValue ?? 0; |
Null Safety with type safety
So if x is -1, then maybeValue is left with null.
That’s not very good for future usage and printing.
So we can use a Type safety where if maybeValue is null, then we assign it to a default number.
Its a shorthand for if-null
1 2 3 4 5 6 7 8 9 10 11 |
int x = -1; int? maybeValue; if ( x > 0) { print('larger than 0'); maybeValue = x; } // if maybeValue is null, then take on 0. maybeValue ??= 0; print(maybeValue); |
Conditional Access Operator
Say we declare an array, and its of type String?.
1 2 3 4 5 |
const cities = <String?>['Shenzhen', 'Beijing', null]; for (var city in cities) { print(city.toUppercase()); // run time crash at index 2 // city is null, and null does not have toUppercase method. } |
in order to fix this, we use conditional access operator:
1 2 3 4 5 6 7 8 |
String? haha; print(haha?.toUpperCase()); // if null, would display null // then const cities = <String?>['Shenzhen', 'Beijing', null]; for (var city in cities) { print(city?.toUpperCase()); // when city is null, it'll simply print it } |