Dart: Why can't constant variables be an instance variable? [duplicate] - dart

This question already has an answer here:
Why only static fields can be declared as 'const'?
(1 answer)
Closed 11 months ago.
https://www.dartlang.org/guides/language/language-tour#final-and-const
In Language Tour of Dart docs, it says "Note: Instance variables can be final but not const. Final instance variables must be initialized before the constructor body starts — at the variable declaration, by a constructor parameter, or in the constructor’s initializer list."
But it's possible to make a constant object using a constant constructor. Why is it not possible to declare a constant variable as a member variable in Dart?

const means compile-time constant.
If you need to create an instance at runtime to create the value, it's not a compile-time constant anymore and therefore its members also can't be const.
Static members do not need an instance to be created and can therefore be constant no matter if the class has a const constructor or if it is used with const or not.

Why is it not possible to declare a constant variable as a member
variable in Dart?
Lets first define what member or instance variables are. Instance variables are basically things that define properties of an object. So, an object of a Car class made like Car(name: "Mercedeces", price: 500000) will have different member property values than Car(name: "Toyota", price: 10000).
Now having an instance variable as final basically means that once you define a Car object with name Mercedes you absolutely can not change the name of the object at run time. You suppose need a Car with name BMW so make a new object. This makes sense when allowing instance properties as final.
Now, lets look at const. const are compile time constants. Suppose you are allowed to define the name instance variable of Car as const. Doing this basically means, no matter how many instances you create of the Car object, all of the names will be the same and just like final members you cannot change it. The first part sounds odd. Not all cars will have the same name, that is contradictory to what instance or object of a class means. A object property may be immutable, using final but definitely will not have the same values. You would want to be able to makes instances of Mercedes sometimes and BMW sometimes. So, it makes no sense to make a instance property as const. This is why dart doesn't allow const without a static keyword. You need a static keyword beside a const property inside a class because only then it conforms to the definition of instance properties.

You can have constants inside a class but they will not be classified as instance variables. The key relies on the difference between final and const, you can't have const instance variables because constants are not variables.
final variables can only be assigned once but this happens at runtime.
the const keyword indicates a compile time constant.
Constants inside classes must be static though, so you could write something like:
class MyClass {
static const kMyConstant = 100;
void talk() {
print('My constant is $kMyConstant');
}
}
This is perfectly valid in dart.

Simply because if you want something const, it has to be done in compile-time, so either:
The entire instance is const: This is done by const-constructor.
The entire instance is not const.
You mark only one field const is meaningless.
E.g. const means "only one" there: you can use Car class to create a lot of Car-instances, then you say your wheel is const, so you share a wheel among all Car?
But you can make Car teslaCar = Car.fromModel(type: "Tesla") get the only one compile-time model const Car.

Related

Error: The instance member ... can't be accessed in an initializer

Why does this code:
class _SequentialTextPageState {
String jsonTextPref = 'seqtext';
int jsonTextSuff = 10;
String jsonText = jsonTextPref + jsonTextSuff.toString();
}
generate these errors?
Error: The instance member 'jsonTextPref' can't be accessed in an initializer.
Error: The instance member 'jsonTextSuff' can't be accessed in an initializer.
It seems to me that concatenation between String and int is correct?
Dart initializes objects in multiple phases. Initializing members directly ("field initializers") occurs early in object initialization, before this becomes valid, so that phase cannot initialize members that depend on other parts of the object.
Dart provides multiple ways to initialize members, so if one member needs to depend on another, you can initialize it in a later phase by using a different mechanism. For example, you could do one of:
Add the late keyword to make the dependent member lazily initialized.
Move initialization of the dependent member into the constructor body.
In the case of a Flutter State subtype, you could initialize the dependent member in its initState method, which in some cases is more appropriate.
Note that in some cases you additionally can consider replacing the member variable with a read-only getter instead. For example, in your case, perhaps you could use:
String get jsonText => jsonTextPref + jsonTextSuff.toString();
That would be appropriate if jsonText should always depend on jsonTextPref and jsonTextSuff, would never need to have an independent value, and if it's acceptable for jsonText to return a new object every time it's accessed.
Dart does not allow field initializers to refer to the object itself. Fields must always be fully initialized before any access is given to the object begin created.
The initializers can only access static and top-level variables, not any instance variables on the object itself.
With null safety, you will be allowed to write late String jsonText = this.something + this.other;. That field will then not be initialized until it's first read or written, which is necessarily after the object itself has been created.
You can only use constant expressions as initializers. x=this.y is not constant.
The error was displayed when I did following:
class MyClass {
String id;
String[] imagePaths;
}
It will mark the String in the line String id; as error, but the error is in the next line, it should be List<String> imagePaths; instead of String[] imagePaths; then the error in the line above also disappears. This can be very confusing if you have a big class and the actual error is many lines underneath the first marked line (talking from experience...)

Can late and final be used together?

I am trying NNBD as of now and I would like to know if you can use the new keyword late and final together.
From what I understood, a late property can be set anywhere. You are basically telling the analyzer that it will not be null when used.
I think that is kinda dangerous in some situations.
So I am wondering if you can add a late final in NNBD, this would tell the analyzer that the property must be initialized within the class constructor.
There is a question similar but I guess there wasn't null safety at the time:
Dart. Late initialize final variables
You can declare a late final variable.
If you declare it with an initializer, late final foo = computeSomething();, then it is a lazy final variable. You can't assign to the variable, but its value is only computed the first time the variable is read. (In my experience, this is never the right choice for local variables, even though the language allows it. If you care about lazy initialization of a local variable, you also almost always want to know whether it was initialized, and a lazy variable doesn't give you that information. It's also confusing that the code is executed out-of-order, and it doesn't allow you to use await in the initializer expression).
If you declare a late final variable without an initializer, you are allowed to write to the variable once. Because the variable is late, the compiler won't complain about assignments at compile-time, unless it's absolutely certain that you have assigned the variable already, and only if it's a local variable (because that's the only variables that the compiler attempts to track assignments to).
If the late final variable without an initializer is an instance member of a class, that means that the class interface has a setter. You need to be very, very careful about exposing late final variables in the public API of a class. (Read: Don't do that!)
It's better to use late variables internally and guard access to the fields, so you can ensure that nobody assigns the variable twice. The goal of a late final variable is not to throw if it's assigned twice. It should never be assigned twice. It's there to allow allow code which knows for some reason that the compiler cannot comprehend, that the variable is only assigning once. So, only allow access to late final variables to code which are aware of that reason, and which maintains the invariant.
Yes!
You can see this pattern commonly used when initializing AnimationController.
class _MyState extends State<MyPage> with SingleTickerProviderStateMixin {
late final AnimationController _controller;
#override
void initState() {
super.initState();
_controller = AnimationController(vsync: this);
}
}
And you can use it for lazy initialization, like:
class Foo {
late final int i = calculate; // Initialized only when used.
int get calculate => ...;
}
Short answer: No, you'll not get any help from the analyzer.
From the nnbd language spec:
It is an error if a top level variable or static variable with a
non-nullable type has no initializer expression unless the variable is
marked with a late or external modifier.
It is an error if a class declaration declares an instance variable
with a potentially non-nullable type and no initializer expression,
and the class has a generative constructor where the variable is not
initialized via an initializing formal or an initializer list entry,
unless the variable is marked with a late, abstract, or external
modifier.
late final int foo; basically turns off null awareness for foo. It seems to be the equivalent of using implicitly unwrapped optionals in Swift, which can be hazardous, if you familiar with that.
https://github.com/dart-lang/language/blob/master/accepted/future-releases/nnbd/feature-specification.md
Besides that, the static analyzer does not warn you about trying to reset a late final.
Let D be a late and final local variable declaration named v. It is a run-time error, throwing an instance of LateInitializationError, to assign a value to v if a value has previously been assigned to v.
https://github.com/dart-lang/language/blob/master/accepted/future-releases/nnbd/feature-specification.md#late-fields-and-variables
Using late means you need to know exactly when things are being initialized and used.

What is the purpose of assigning `const` value to a `final` variable in dart? [duplicate]

This question already has answers here:
What is the difference between the "const" and "final" keywords in Dart?
(16 answers)
Closed 4 years ago.
So I was doing the first example for Flutter, and under
Step 4: Create an infinite scrolling ListView,
I encountered this piece of code:
class RandomWordsState extends State<RandomWords> {
final _suggestions = <WordPair>[];
final _biggerFont = const TextStyle(fontSize: 18.0);
...
}
But I found the following line a little spooky.
final _biggerFont = const TextStyle(fontSize: 18.0);
My question is, what is the purpose of assigning a constant value to a final variable?
I know that
Compile-time constants are canonicalized,i.e. no matter how many times
you write const MyObj(0, 0),you only create one object.
This may sound useful, but you can simply create the const variable to hold the value and use that variable instead.
Well, don't you think it's kinda redundant? I get it that the developers at Flutter wanted to create a compile-time constant object, but hey! you are assigning that value to a final variable. Which is somewhat the same thing.
Any thoughts?
UPDATE
I googled some definitions, I found that
const constructors cannot have a body and It's class must not have
any non-final fields
So is this the reason why we used the const keyword? Because if you'll look at the TextStyle class's design, you'll realize that they have done the exact same thing here.
I personally think
final _biggerFont = const TextStyle(fontSize: 18.0);
looks like a mistake, and that that alone is reason enough to change it.
The member is only used inside the same class, so there is no reason not to make it static. Then it will not take up one extra memory location for each instance of the class, all pointing to the same value. (That's assuming the compiler doesn't recognize the field as always having the same value, and just inlines the value everywhere).
If the member is static, it might as well also be const, so I'd write it as:
static const _biggerFont = TextStyle(fontSize: 18.0);
This assumes that the code is what I found by searching for final _biggerFont = const.
If there is a subclass in the same library which overrides _biggerFont, then it does need to be an instance variable. It could still be a getter instead of a field, then. Whether that's an efficiency improvement depends on how the class is used, and how well a compiler optimizes a final field that always has the same value.
In any case, creating a private instance member which always has the same constant value looks like something that should just be a static constant to begin with, and code that looks like a mistake is confusing to read. I'd rewrite it (or document it) just for that reason - to avoid the reader being confused about why it is the way it is.
const values are canonicalized.
This means no matter how often your code contains
final _biggerFont = const TextStyle(fontSize: 18.0);
there will only a single const TextStyle(fontSize: 18.0) instance.
Using const for class' fields requires static, this would not allow to access its value using a reference to an instance of RandomWordsState.
There are different preferences for classes with const constructors
http://dart-lang.github.io/linter/lints/avoid_field_initializers_in_const_classes.html
AVOID field initializers in const classes.
Instead of final x = const expr;, you should write get x => const
expr; and not allocate a useless field. As of April 2018 this is true
for the VM, but not for code that will be compiled to JS.
and top-level fields
http://dart-lang.github.io/linter/lints/prefer_const_declarations.html
PREFER using const for const declarations.
Const declarations are more hot-reload friendly and allow to use const
constructors if an instantiation references this declaration.
The IDE also suggests to replace final with const for local variables that are initialized with constant values
I haven't found where this suggestion comes from, but it makes sense because local variables can be const without static

Get type of variable at run-time without having it assigned to an instance of an object and without mirrors

Is it possible to get the type of a variable in Dart at run-time, without having it assigned to an instance of an object and without using mirrors?
I know I can do this without mirrors (which is great!):
Foo foo;
foo = new Foo();
var fooType = foo.runTimeType; // This will give me a type of "Foo"
But I want to know the type of the variable before it is assigned to an object:
Foo foo;
var fooType = foo.runTimeType; // This will not give me a type of "Foo"
I am guessing it is not possible since the typing info is lost in run-time but would like to have it confirmed.
(My actual scenario is that I am doing dependency injection into a Polymer Element using Custom Events. I would like to put as much of this code as possible in an element base-class and have as little code as possible in each derived element class. One thing that I need to do this is to know the type of variables that are to be injected).

Are "class var"s initialized to zero?

I know that, in Delphi, instance variables and global variables are initialized to zero (this has been asked here before).
However, what about static variables (class var)? I would expect class vars to be initialized to zero, just like global variables. But I've seen too many new Delphi compiler features that were still half-baked to assume that it works, without documentation that actually states a guarantee.
The Help has no index entry for "class var". The "Fields" topic mentions class fields, but does not specify whether they're initialized at program startup. And the obvious fix, of explicitly initializing them (class var X: Integer = 0;), doesn't compile ("';' expected but '=' found").
Are class variables initialized to zero? Is there documentation that explicitly states this?
I'm not aware of any documentation that explicitly states it, but class vars are just a special type of global variable, and globals get zeroed.

Resources