Why a final field should be initialised when it is made nullable? - dart

class A {
final int? b; // Error
}
Error:
The final variable 'b' must be initialized.
Since the field b is nullable, it could be left uninitialised and hence needs no initialisation work. But it gives an error.

Not initializing a final field, regardless of whether it's nullable, is almost certainly a mistake. This was not different before null safety was introduced.
Suppose that final fields were allowed to be implicitly initialized to null. Then in a class such as:
class A {
final int? b;
}
there would be no point for b to exist at all since it'd always be null.
But maybe A provides a constructor that could initialize b to a non-null value:
class A {
final int? b;
A();
A.nonNull() : b = 42;
}
But now there's no way to tell if the default A() constructor actually wants b to be initialized to null or if the programmer just forgot to initialize it. The latter is much more likely, so Dart errs on the side of requiring explicit initialization.

You need to initialize b first like int b or string b; Assigning value is optional.

Related

Dart equatable with non final variables

I have class with final variable (hash) and some other non-final variables.
Hash is unique value. And objects are stored in Set. Set uses '==' operand to check equality of objects. I want to override "==" and "hashCode" in my class and work with Set array.
To avoid using boilerplate code I want to use Equatable extension. Like this
class User extends Equatable {
final String hash;
String balance;
bool state;
....
#override
List<Object> get props => [hash];
} .... Set<User> users
Is it correct way to use Equatable in my case, 'cause it is uses with immutable classes.
Thanks!
Overriding hashCode to depend on non-final fields is usually not recommended because it can make Sets and Maps and other data structures that depend on hashCode internally inconsistent. Suppose you have such an object and insert it into a Set. Later, you mutate that object by assigning a new value to that field, but the Set would still have a reference to that object with the old hash code. For example, consider:
class Foo {
String s;
Foo(this.s);
#override
bool operator ==(Object other) {
return other is Foo && s == other.s;
}
#override
int get hashCode => s.hashCode;
#override
String toString() => s;
}
void main() {
var foo = Foo('foo');
var someSet = <Foo>{foo};
foo.s = 'bar';
print(someSet.contains(foo)); // Prints: false
someSet.add(foo);
print(someSet.length); // Prints: 2
print(someSet); // Prints: {bar, bar}
}
and now someSet would have two references to the exact same object, which violates its goal of storing unique objects.
A Map would have similar problems.
Since hashCode is tied to operator ==, this consequently also means that you usually shouldn't override operator == to depend on non-final fields.
You can get away with it if you can guarantee that you never mutate your objects while they're being referenced by a Set/Map/etc. or if you can guarantee that whenever you want to add your object to a Set/Map/etc. that you create a copy of your object and add that copy instead.
I'd say your use is correct.
You use Equatable only with the final field, so the equality and hash code should be stable over time, and the hash field is unique, so it can serve as identifier for the object.
If your hash field ends up not unique, you'll have two distinguishable objects that are equal, so ... don't do that.
Not sure how much you gain from using Equatable, though. If you wrote the equality and hash code yourself, it would just be:
bool operator==(Object other) => other is Foo && hash == other.hash;
int get hashCode => hash.hashCode;

How do I initialize non-nullable members in a constructor body?

I've created my class in Dart this way, but I'm getting the Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'. I would like to know if there's a way to do it in a 'Python' style where this kind of class creation is possible, thank you in advance.
class Lexer {
String _text;
int _pos;
String _current_char;
Lexer(String text) {
this._text = text;
this._pos = -1;
this._current_char = '';
this.advance();
}
void advance() {
this._pos++;
this._current_char = this._pos < this._text.length ? this._text[this._pos] : '';
}
}
class Lexer {
String _text;
int _pos;
String _current_char;
This declares several members with type String. Since they are declared as String and not as String?, these members are non-nullable; they are not allowed to ever be null. (This is part of the new null-safety feature from Dart 2.12.)
Dart initializes objects in two phases. When the constructor's body runs, Dart expects all member variables to already be initialized. Because your members are non-nullable and haven't been initialized to non-null values yet, this is an error. The error message explains what you can do:
Non-nullable instance field 'text' must be initialized. Try adding an initializer expression, or add a field initializer in this constructor, or mark it 'late'.
Use initializer expressions. This means using an initializer list:
Lexer(String text)
: _text = text,
_pos = -1,
_current_char = '' {
advance();
}
Note that if you're initializing members with a construction parameter of the same name, you can use shorthand:
Lexer(this._text)
: _pos = -1,
_current_char = '' {
advance();
}
Adding field initializers. This means initializing members inline in the class declaration.
class Lexer {
String _text = '';
int _pos = -1,
String _current_char = '';
Marking your members as late. This means that you promise that the variables will be initialized before anything attempts to use them.
class Lexer {
late String _text;
late int _pos,
late String _current_char;
Making your members nullable, which allows them to be implicitly null by default:
class Lexer {
String? _text;
int? _pos,
String? _current_char;
However, that will require that all accesses explicitly check that the members aren't null before using them.
You also might want to read: Dart assigning to variable right away or in constructor?

how do you assign generic type variable to a know type?

Consider that I have a two class and a enum.
one of its class have a parameter as Enum type
enum A { B, C }
class Q {
Q(A a);
}
and Another class is of generic type T which take a one parameter as T type in constructor. Question is how do you convert this class's parameter type T into A inside of its own class ?
class C<T> {
T a;
C(this.a);
void getType(){
if(T == A) Q(a); /* The arugument type 'T' can't be assigned to Parameter type 'A'
cause Class Type is still 'T' */
}
}
This code contain more than 4 enum and each class for its Type. For reproducible purpose, I reduced to one.
Do an explicit cast to A. Doing a comparison of types like this likely doesn't do implicit type conversion like it would when checking for null in null-safe Dart. Dart isn't able to see that a is actually of type A even with your if statement.
So just do a as A.
class C<T> {
T a;
C(this.a);
void getType(){
if(T == A) Q(a as A);
}
}

How to create nullable variable in Dart

I created a model class, this is one of my a variable in model class
Datetime hotel_effect_open_date
but JSON response hotel_effect_open_date is null, getting an error in my application. and I modified to DateTime to String, it's working. In API created in the .net core, it looks like this,
Nullable<DateTime> hotel_effect_open_date
How to create a nullable variable in DART language?
Now Dart is in the process of redesigning its type system. So that expression of that type can be either nullable or non-nullable. Something like this:
type? variable; // variable can be null.
Example:
int? a; // a can now be null.
Reference: Dart nullability syntax decision
TLDR:
int? aNullableInt = null;
Detailed explanation:
Dart 2... and above
documentation:
https://dart.dev/null-safety
With null safety, all of the variables in the following code are
non-nullable:
// In null-safe Dart, none of these can ever be null.
var i = 42; // Inferred to be an int.
String name = getFileName();
final b = Foo();
To indicate that a variable might have the value null, just add ? to
its type declaration:
int? aNullableInt = null;
Dart <2
DateTime example;
example = null;
Uninitialized variables have an initial value of null. Even variables with numeric types are initially null, because numbers—like everything else in Dart—are objects.
int lineCount;
assert(lineCount == null);

Why does `if (var = null)` compile in dart?

I've recently came across this question How do I solve the 'Failed assertion: boolean expression must not be null' exception in Flutter
where the problem comes from a should be invalid code that gets treated as valid.
This code can be summarized as :
int stuff;
if (stuff = null) { // = instead of ==
}
But why does this code compiles ? As the following will not.
int stuff;
if (stuff = 42) {
}
With the following compile error :
Conditions must have a static type of 'bool'.
So I'd expect out of consistency that if (stuff = null) to gives the same error.
null is a valid value for a bool variable in Dart, at least until Dart supports non-nullable types.
bool foo = null;
or just
bool foo;
is valid.
Therefore in the first case there is nothing wrong from a statical analysis point of view.
In the 2nd case the type int is inferred because of the assignment, which is known to not be a valid boolean value.
bool foo = 42;
is invalid.
When you say var stuff; with no initial value it is giving stuff a static type of dynamic. Since dyamic might be a bool, it's legal to assign null to a variable of type dynamic, and it's legal to use a possibly null bool in a conditional, the compiler doesn't flag this. When you say int stuff; the compiler knows that stuff could not be a bool. The reported error in that case is cause by the static type of stuff, not the assignment to null.
Edit: Got the real answer from someone who knows how to read the spec.
The static type of an assignment expression is the right hand side of the assignment. So the expression stuff = null has the static type of Null which is assignable to bool.
The reasoning is that the value of an assignment is the right hand side, so it makes sense to also use it's type. This allows expressions like:
int foo;
num bar;
foo = bar = 1;
Commonly assignment operation returns the value that it assigns.
int a = 0;
print(a = 3);//Prints 3
So,
When stuff = null,
'stuff = null' returns null. if statement needs a boolean .null is a sub-Type of boolean.
if(null){}
is valid
When stuff = 42,
'stuff = 42' returns 42. if statement needs a boolean .42 is not a sub-Type of boolean.
if(42){}
is not valid

Resources