Modifying final fields inside the class in Dart
Asked Answered
Y

3

18

Dart docs read:

If you never intend to change a variable, use final or const, either instead of var or in addition to a type. A final variable can be set only once;

Ok, this means that assigning a final variable second time will not work, but nothing is said about modifying, i.e.

void main() {
  final List<int> list = [1,2,3];  

  list = [10, 9, 8]; //error!

  list
  ..clear()
  ..addAll([10, 9, 8]); //works!
}

As one can see, in essence, I re- assigned final variable list. Doesn't it contradict the whole idea of final variables?

Yellowtail answered 7/10, 2014 at 12:19 Comment(0)
L
22

final doesn't mean deep final.

The list variable references still the same list instance even when you modify the lists content. Any mutable instance can be modified even when it was assigned to a final variable. Imagine

void main() {
  var l = [1,2,3];
  final List<int> list = l;  
}

Now you wouldn't be able to modify the items in the list referenced by l because the list is also assigned to the final field list (both list and l reference the same list). That doesn't make sense.

What you can do to make the list immutable is

final List<int> list = const[1,2,3];  

Now you can't assign another list to list and you can't modify the contents of the list referenced by list.

An alternative way

  import 'dart:collection'
  ...
  var l = [1,2,3];
  final List<int> list = UnmodifiablyListView(l);

Now you can't modify list or the contents of the list referenced by list but you can modify the contents referenced by l (list would reflect the changes made to l).
If you loose the reference to l you have no way of modifying the contents.

  var l = [1,2,3];
  final List<int> list = UnmodifiablyListView(l);
  l = null;

final is nice when you for example want to ensure that the list field never is set to null.

class MyModel {
  final list = [];
}

The list field is public but nobody can set list to for example null.

var model = new MyModel();
...
model.list.forEach(print);

will never fail with an exception like null doesn't have a method 'forEach'.
This is similar but more concise than

class MyModel {
  var _list = [];
  List get list => _list;
}
Laggard answered 7/10, 2014 at 12:28 Comment(3)
However, if one codes void main() { final List<int> list = [1,2,3]; List<int> l=list; l=[10,9,8]; } then at l=[10,9,8] a deep copy of list will be created; One can see that clearly in the debugger: at line 2 id's of l and list will be the same, i.e. they refer to the same object in memory, while at line 3, these references will be different. I agree, your first example does not make any sense, but the bahaviour I described does not seem to be very meaningful either, i.e. why at all have finals if there is such a simple way to re-assign them? Prev comment didn't come out right.Yellowtail
I don't know why you think a deep copy is created. With l = [10,9,8]; you created a new (2nd) list with different values. list still refers the first list and l refers a completely different list.Pachton
yes, i misinterpreted the case. thanks for explanations.Yellowtail
D
5

This is a const vs final confusion. Here's the difference:

final - means you can't use the operator =. But you can add, or remove items. The assignment is final, however, the values are NOT constant.

Example:

void main() {
  final List<int> list = [1,2,3];  

  print(list); // [1, 2, 3]
  list.add(5); // Works!
  print(list); // [1, 2, 3, 5]
}

const - means you can't re-assign nor change the values.

void main() {
  const List<int> list = [1,2,3];  

  print(list); // [1, 2, 3]
  list.add(5); // Uncaught Error: Unsupported operation: add
  print(list);
}

Durra answered 20/6, 2022 at 23:51 Comment(0)
D
-1

In addition to Adrian's answer, I am just starting out with Dart and I had a little bit of trouble understanding it until I broke it down for myself. The final keyword cannot be changed when it has been assigned to a variable(use this preferably at run-time) to initialize.[they are often instance variables] The const keyword is preferably used for class variables(static const) and declaring constant variables. they cannot be changed at all until run-time initialization. But when we get to lists, the explanations for final and const become more narrow. final lists cannot be changed only modified using .add() or .remove(), meaning the assignment is final but the values are not const lists and cannot be mutated in any way, they are final and that closes the use of the initialized list. I hope this helps. https://dart.dev/guides/language/language-tour#variables

Drumfish answered 22/6, 2022 at 14:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.