Dart中reference type、value type、canonicalized的具体定义咨询
术语来源与定义
Dart官方没有针对reference type、immutable、canonicalized这三个术语做Dart专属的自定义解释,三者都是编程语言理论领域的通用概念,并非JavaScript、Swift或某一门特定语言的独有设计,学习Dart不需要提前掌握其他语言的对应概念,直接结合Dart的行为理解通用定义即可:
- reference type(引用类型):指变量存储的是对象实例的内存引用,而非对象本身的完整数据拷贝,Dart中所有类型都符合这一特征,所有变量本质上都指向一个类的实例对象。
- immutable(不可变):指对象实例一旦创建完成,其内部所有状态、属性都无法被修改,任何看似“修改”该对象的操作,本质都是生成并返回一个全新的对象实例,不会改动原对象的任何内容。
- canonicalized(实例规范化):指对于同类型、值相等的不可变对象,编译器或运行时会全局仅维护一份唯一实例,不会重复创建多个值完全相同的冗余对象。
canonicalized的实际含义与代码现象解释
首先明确:canonicalized不等同于“值是const常量”,你贴出的测试代码运行后outer值不变的现象,和canonicalized特性没有直接关系,是引用类型赋值规则+int不可变两个特性共同导致的:
void main() { int outer = 0; int other = outer; other = 1; print(outer); // 0 (not changed) }
上述代码中,int other = outer只是把outer存储的、指向值为0的int实例的引用拷贝了一份给other变量;后续other = 1的操作,本质是修改other变量存储的引用,让它指向值为1的int实例,全程没有对值为0的原int对象做任何修改——哪怕int没有做canonicalized处理,只要它满足不可变的特性,这段代码的输出结果依然是0,这个用例无法验证canonicalized特性。
Dart中对int、double、String、bool做canonicalized处理的实际表现是:只要这几类对象的值相等,无论实例是编译期const生成,还是运行时计算生成,都会指向全局唯一的同一份实例。可以用identical方法验证:
void main() { const int a = 2; const int b = 1 + 1; print(identical(a, b)); // true,const常量默认做规范化 int c = 2; int d = 1 + 1; // 运行时计算得到值2 print(identical(c, d)); // 同样为true,普通声明的int只要值相等就指向同一份实例 }
作为对比,普通的自定义不可变类,只有const声明的实例会被规范化,非const生成的同值实例是不同对象:
class ImmutablePoint { final int x, y; const ImmutablePoint(this.x, this.y); } void main() { const p1 = ImmutablePoint(1, 2); const p2 = ImmutablePoint(1, 2); print(identical(p1, p2)); // true,const常量规范化 var p3 = ImmutablePoint(1, 2); var p4 = ImmutablePoint(1, 2); print(identical(p3, p4)); // false,非const创建的同值实例不会自动规范化 }
正是因为int、double、String、bool同时满足不可变+实例规范化两个特征,它们虽然本质是引用类型,使用时却完全不会出现普通引用类型的副作用,表现和值类型完全一致。
内容的提问来源于stack exchange,提问作者森口万太郎

