健全类型系统

Dart 健全类型安全(Sound Type System)

核心定义

Dart 属于健全静态类型语言,依靠「静态编译检查 + 运行时校验」双重保障:变量运行时的值一定和它静态声明类型匹配。

类型规则强制生效,但类型注解可省略,由类型推断自动推导;

编译期静态分析提前拦截绝大多数类型错误。

什么是健全性(Soundness)

代码不会进入非法类型状态;若表达式静态类型是 String,运行时一定只能拿到字符串,不会出现其他类型。 同类语言:Java、C#。

  • 编译期拦截:int a = "abc"; 直接报错;
  • 运行时拦截:使用 as 强制转换类型不匹配时抛出异常。

四大优势

编译期暴露类型 bug,规避难复现的线上类型崩溃;

代码可读性更高,类型不会 “欺骗开发者”;

代码易维护,修改一处代码,类型检查提示所有受影响代码;

AOT 编译效率更高,类型信息帮助编译器生成更高效机器码。

例子

动态集合引发静态报错

// 预期接收 List<int>
void printInts(List<int> a) => print(a);

void main() {
  // 无泛型声明,自动推断为 List<dynamic>
  final list = [];
  list.add(1);
  list.add('2'); // 动态列表可混存类型
  printInts(list); 
  // 编译报错:List<dynamic> 不能赋值给 List<int>
}Code language: Dart (dart)

显式声明泛型约束

void printInts(List<int> a) => print(a);

void main() {
  final list = <int>[]; // 显式指定泛型int
  list.add(1);
  // list.add('2'); // 编译报错,无法放入字符串
  list.add(2);
  printInts(list); // 正常通过静态检查
}Code language: Dart (dart)

三大核心规则

1 重写方法时,返回类型必须是原类型或其子类型(生产者规则)

父类方法返回值是生产者,子类可以返回更具体的子类型,不能返回无关类型。

例如

正确示例

class Animal {
  Animal get parent => Animal();
}

class HoneyBadger extends Animal {
  @override
  HoneyBadger get parent => HoneyBadger(); // 子类型,合法
}Code language: Dart (dart)

错误示例

class Root {}
class HoneyBadger extends Animal {
  @override
  Root get parent => Root(); // 无关类型,编译报错
}Code language: Dart (dart)

2 重写方法时,参数类型必须是原类型或其父类型

方法入参是消费者,子类只能放宽参数范围(父类型),不能缩小为子类型;缩小会破坏类型安全。

正确示例(参数放宽到 Object)

class Animal {
  void chase(Animal a) {}
}

class HoneyBadger extends Animal {
  @override
  void chase(Object a) {} // Object是Animal父类,合法
}Code language: Dart (dart)

错误示例(参数缩小为子类 Mouse)

class Mouse extends Animal {}
class Cat extends Animal {
  @override
  void chase(Mouse a) {} // 缩小参数类型,静态报错
}

void main() {
  Animal cat = Cat();
  cat.chase(Alligator()); // 理论上允许传入Alligator,造成类型不安全
}Code language: Dart (dart)

规则 3:不能把动态泛型集合直接赋值给确定泛型集合

List<dynamic>List<Dog> 无法直接赋值给 List<Cat>,编译报错;不同泛型集合不存在隐式转换。

class Cat {}
class Dog {}

void main() {
  List<Cat> foo = <dynamic>[Dog()]; // 静态报错
  List<dynamic> bar = <dynamic>[Dog(), Cat()]; // 合法,动态列表可混合类型
}Code language: Dart (dart)

运行时类型校验

静态检查无法覆盖全部场景,运行时会额外校验类型,不匹配抛出异常。

泛型强制转换运行时报错

class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}

void main() {
  List<Animal> animals = <Dog>[Dog()];
  List<Cat> cats = animals as List<Cat>; 
  // 运行时异常:无法将List<Dog>强转为List<Cat>
}Code language: Dart (dart)

dynamic 隐式向下转换(运行校验)

dynamic 变量赋值给具体类型会隐式转换,运行时校验类型,不匹配抛错。

int assumeString(dynamic object) {
  String string = object; // 运行时校验object是否为String
  return string.length;
}

void main() {
  assumeString("test"); // 正常执行
  assumeString(123);    // 运行时报错:int不能转为String
}Code language: Dart (dart)

关闭隐式转换(严格类型模式)

analysis_options.yaml 开启严格转换,禁止 dynamic 隐式向下转

analyzer:
  language:
    strict-casts: trueCode language: JavaScript (javascript)

类型推断 Type Inference

Dart 自动推导变量、泛型、方法返回值类型;信息不足时默认推断为 dynamic

普通变量推断

// 自动推导 Map<String, Object>
var arguments = {'argA': 'hello', 'argB': 42};

var x = 3; // x 推断为 int
x = 4.0;   // 报错,int不能存double

num y = 3; // 显式声明num,可存int/double
y = 4.0;   // 合法Code language: Dart (dart)

泛型构造 / 集合推断

List<int> listOfInt = []; // 自动推导 <int>[]
var listOfDouble = [3.0]; // 推导 List<double>

var ints = listOfDouble.map((x) => x.toInt()); 
// x推导为double,回调返回int,ints推导为Iterable<int>Code language: Dart (dart)

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注