The meta.dart
package defines an @immutable
annotation that can be used to mark classes that are immutable, meaning that all their instance variables, directly defined or inherited, have to
be final
, that is, once the instance is created, it cannot be
modified.
However, like any other annotation, the @immutable
annotation only provides information to the developer using the class and to
developer tools such as the analyzer package, and it does not enforce any constraint.
Therefore, there is nothing that prevents a developer from creating a mutable class and marking it as @immutable
, as in the following
example:
import 'package:meta/meta.dart';
@immutable
class Point {
int x;
int y;
Point(this.x, this.y);
}
void main() {
var p = Point(2, 3);
p.x = 4;
print(p.x); // Will print 4
}
This is a problem because it can lead to confusion and bugs, as developers might expect the class to be immutable and not realize that it is
actually not.
The best way to prevent this from happening is to make sure that classes marked with the @immutable
annotation only have const
constructors. This will in turn ensure that all instance
variables are final
, hence the instance is truly immutable.
import 'package:meta/meta.dart';
@immutable
class Point {
final int x; // Final required by the const constructor
final int y; // Final required by the const constructor
const Point(this.x, this.y);
}
void main() {
const p = Point(2, 3);
// p.x = 4; // This will now be a compile-time error
print(p.x);
}
What is the potential impact?
Even if the @immutable
class is truly immutable, missing the const
constraint on its constructors will lead to subpar
performance, as the Dart compiler will not create compile-time constants in a non-constant context, and will raise a compile-time error in a const
context.
import 'package:meta/meta.dart';
@immutable
class Point {
final int x;
final int y;
Point(this.x, this.y); // Missing const
}
void main() {
const p = Point(2, 3); // Compile-time error
const list = [Point(2, 3)]; // Compile-time error
var list = const [Point(2, 3)]; // Compile-time error
}