Dart中省略或指定基类时带类型参数的构造/静态函数拆离为何不相等
现象根本原因
这个现象的核心是Dart对泛型构造/泛型方法的拆离(tear-off)和泛型构造调用的类型处理规则完全不同,结合identical的严格相等判定逻辑导致的,具体可以拆成三个规则层面的原因:
- 首先明确:
identical判定两个对象相等的前提是,二者是同一个运行时对象,拥有完全一致的运行时类型和内部状态。 - 第一,泛型构造的调用场景和拆离场景的类型推导逻辑不同:
当你写const MyClass()或者const MyClass.new()这种构造调用表达式时,编译器必须拿到确定的类型实参才能生成实例,在无额外上下文的情况下,会直接用泛型参数的上界num填充省略的类型参数,因此const MyClass()和const MyClass<num>()是完全等价的const调用,经过const规范化后会指向同一个常量实例,二者绑定的实例方法拆离自然也是同一个对象,这就是前三个true结果的来源。
但当你写MyClass.new这种构造拆离表达式时,如果不显式指定类的泛型参数,拿到的不是“默认填上界num的具体构造函数”,而是保留了完整泛型形参的泛型函数,它的运行时类型是MyClass<T> Function<T extends num>(),你在调用它的时候仍然可以传入int、double等符合边界的类型参数得到对应类型的实例;而MyClass<num>.new是已经把泛型参数T固定绑定为num的具体函数,运行时类型是MyClass<num> Function(),调用时不能再传入其他类型参数。二者运行时类型完全不同,不可能是同一个对象。 - 第二,泛型静态方法的拆离逻辑和泛型构造完全一致:
MyClass.staticFunction是直接拆离的原始泛型方法,运行时类型是void Function<T extends num>(),保留了泛型形参;MyClass.staticFunction<num>是把泛型形参T绑定为num后得到的实例化闭包,运行时类型是void Function(),二者同样是完全不同的运行时对象。 - 第三,Dart语言规范对拆离对象的相等性有明确规定:
- 从同一声明位置拆离得到的同一个未实例化泛型函数/构造,永远相等,因此
identical(MyClass.new, MyClass.new)、identical(MyClass.staticFunction, MyClass.staticFunction)这类表达式返回true。 - 从同一声明位置拆离、绑定了完全相同的类型实参和实例上下文的实例化函数/构造,永远相等,因此
identical(MyClass<num>.new, MyClass<num>.new)、identical(MyClass.staticFunction<num>, MyClass.staticFunction<num>)这类表达式返回true。 - 未实例化的泛型拆离对象,和任何绑定了具体类型实参的实例化拆离对象,永远不相等,这就是观察到的两个
false结果的直接规则依据。
- 从同一声明位置拆离得到的同一个未实例化泛型函数/构造,永远相等,因此
常见误区:很多人会默认“省略泛型参数就等于填上界”,这个规则只适用于需要生成具体类型、具体实例的场景(比如构造调用、类型注解),不适用于泛型函数/构造的拆离场景——拆离时省略类型参数,拿到的是可后续实例化的通用泛型函数,不是预先绑定了上界的具体函数。
内容的提问来源于stack exchange,提问作者hacker1024
相关产品推荐
相关产品推荐

