如果常量构造函数在常量上下文之外并且在没有const的情况下被调用,那么该类实例的所有字段仍然是最终值还是常量值,会发生什么?我知道它将是一个不恒定的物体。
MaterialApp(); // Do NOT create a constant
由MaterialApp
包装的const
变量仍然是const
。
当可能时,最好将值设置为const
具有const
构造函数的类必须只有final
(不可重新分配(字段,并且这是类接口的一部分(即,该类只为这些字段提供getter,而不提供setter(。
该类的实例是否为编译时常数不会改变这一点。
const
构造函数的工作原理与任何其他构造函数一样。
const
构造函数对初始值设定项列表(可能只有常量表达式(和body(没有body!(有一些额外的要求,并对周围的类设置了一些限制(所有字段都必须是final
(。
任何类都可以满足这些限制,无论它是否有const构造函数。
当const
构造函数被调用为非const
时(不在const
上下文内,前面没有const
(,它的工作方式与任何其他构造函数一样,在一个与任何其他类一样工作的类上。那么,您可以完全忽略const
,只需像对待其他类和构造函数一样对待它们。
非重定向生成构造函数(实际初始化对象状态的real构造函数,而不是重定向或工厂构造函数(仍将初始化类字段。这些字段仍然是最终字段。
构造函数的const
调用和非const
调用之间的区别不在于构造函数做什么,而在于前后发生了什么。
在const
调用中,所有参数也必须是常量表达式,计算结果为常量值。初始值设定项列表的潜在常量表达式也只能创建常量值。这确保了所有最终字段都用其他常数值初始化,这再次确保了生成的对象是完全不可变的。对没有正文和非常受限的初始值设定项列表表达式的限制是为了确保在评估过程中只能调用非常简单的用户代码表达式和特定的平台库方法。这允许编译器在编译程序之前假装对构造函数求值。
此外,在const
调用之后,生成的对象被规范化。如果编译器已经创建了另一个相同类型的常量对象,在每个字段中都有相同(规范化后相同(的值时,它会丢弃新对象并使用现有对象。这就是要求字段为final
的原因。如果对象是可变的,规范化就没有意义了,因为你可以通过改变一个对象来区分两个对象。当对象是不可变的时,规范化不会改变程序行为。
这两个区别显而易见:
class C {
final List<int> list;
const C(this.list);
}
void main() {
const c1 = C([1]);
var c2 = const C([1]);
print(identical(c1, c2)); // true
print(identical(c1.list, c2.list)); // true
var v1 = C(c1.list); // Deeply immutable, not canonicalized.
print(identical(c1, v1)); // false
var v2 = C([1]);
print(identical(c1, v2)); // false
print(identical(v1, v2)); // false
v2.list.add(42);
print(v2.list); // [1, 42] // Not deeply immutable.
}