Dart 关键字解析:mixin、abstract、on、with

259 阅读3分钟

Dart 关键字解析:mixin、abstract、on、with

1. mixin 关键字

定义

mixin 是 Dart 提供的一种机制,允许在多个类之间复用代码。与类不同,mixin 不能被实例化,而是用来被其他类混入使用。

用法

mixin Logger {
  void log(String message) {
    print("Log: $message");
  }
}

class Service with Logger {
  void doSomething() {
    log("Service is doing something");
  }
}

class AdvancedService extends Service {
  void doAdvancedWork() {
    log("Advanced service is working"); // ❌ 这里会报错
  }
}

void main() {
  Service service = Service();
  service.doSomething(); // 输出: Log: Service is doing something
}

mixin 不会被继承,它只能被直接混入 (with)。如果 Service 作为一个类混入了 Logger,但 Service 的子类 不会 继承 Logger 的功能。

2. abstract 关键字

定义

abstract 用于定义抽象类,表示该类不能被直接实例化,只能作为基类被继承。

用法

abstract class Animal {
  void makeSound(); // 抽象方法,不提供实现
}

class Dog extends Animal {
  @override
  void makeSound() {
    print("Woof!");
  }
}

void main() {
  Dog dog = Dog();
  dog.makeSound(); // 输出: Woof!
}

3. abstract mixin 关键字

定义

Dart 允许 mixin 声明为 abstract mixin,表示该 mixin 本身是抽象的,不能直接被使用,必须由子类实现其中的方法。

用法

abstract class BaseLogger {
  void setup(); // 仅作为对比,展示 abstract class
}

abstract mixin Logger on BaseLogger { 
  void log(String message);
}

class ConsoleLogger extends BaseLogger with Logger { 
  @override
  void setup() {
    print("Logger setup completed.");
  }

  @override
  void log(String message) {
    print("Console Log: $message");
  }
}

void main() {
  ConsoleLogger logger = ConsoleLogger();
  logger.setup(); // 输出: Logger setup completed.
  logger.log("Hello, Dart!"); // 输出: Console Log: Hello, Dart!
}

解释

  1. abstract mixin Logger on BaseLogger

• 这里 on BaseLogger 规定了 Logger 只能混入 BaseLogger 或其子类。

• 这避免了 Logger 被滥用于任何类,而是强制它只能在 BaseLogger 体系内使用。

  1. ConsoleLogger extends BaseLogger with Logger

• ConsoleLogger 继承了 BaseLogger,并通过 with Logger 混入 Logger,必须实现 log 方法。

• 如果 Logger 只是 abstract class,那么 ConsoleLogger 就需要 extends Logger,这样就无法同时继承 BaseLogger。

• 通过 abstract mixin,我们可以在继承 BaseLogger 的同时引入 Logger 这个功能模块,提高代码复用性。

什么时候应该使用 abstract mixin?

需要 mixin 机制:如果你希望将某个功能模块化,并允许多个类通过 with 关键字使用它,而不是强制单一继承。

强制约束特定基类:通过 on 关键字,abstract mixin 限制了它的适用范围,避免了错误使用。

强制子类实现方法:不像普通 mixin 允许提供默认实现,abstract mixin 要求 with 它的类必须实现它的抽象方法。

结论

虽然 abstract mixin 乍一看与 abstract class 类似,但它适用于更灵活的代码复用场景,特别是当你希望一个类既能继承一个类,又能混入多个功能时,它提供了一种比 abstract class 更优雅的解决方案。

4. on 关键字

定义

on 关键字用于限定 mixin 只能被特定的类或其子类混入。

用法

abstract class Animal {
  void breathe() {
    print("Breathing...");
  }
}

mixin Walker on Animal {
  void walk() {
    print("Walking...");
  }
}

class Dog extends Animal with Walker {}

void main() {
  Dog dog = Dog();
  dog.breathe(); // 输出: Breathing...
  dog.walk(); // 输出: Walking...
}

注意:如果 Walker 没有 on Animal 限制,则 mixin 可以被任何类混入。

5. with 关键字

定义

with 关键字用于在类中混入 mixin,实现代码复用。

用法

mixin Flyer {
  void fly() {
    print("Flying...");
  }
}

class Bird with Flyer {}

void main() {
  Bird bird = Bird();
  bird.fly(); // 输出: Flying...
}

6. mixin 中包含字段

定义

Dart 3.0 之后,mixin 允许包含字段,但字段必须是 late 或有初始值。

用法

mixin Counter {
  int count = 0;
  void increment() {
    count++;
    print("Count: $count");
  }
}

class CounterService with Counter {}

void main() {
  CounterService service = CounterService();
  service.increment(); // 输出: Count: 1
  service.increment(); // 输出: Count: 2
}

late 关键字

late 关键字用于延迟初始化变量,表示变量在首次使用前会被赋值,而不是在声明时初始化。这在 mixin 中尤为重要,因为 mixin 不能有构造函数,不能在初始化时直接赋值。

用法示例:
mixin Configurable {
  late String config;
  void setConfig(String value) {
    config = value;
  }
  void printConfig() {
    print("Config: $config");
  }
}

class Service with Configurable {}

void main() {
  Service service = Service();
  service.setConfig("Dark Mode");
  service.printConfig(); // 输出: Config: Dark Mode
}

定义

Dart 3.0 之后,mixin 允许包含字段,但字段必须是 late 或有初始值。

用法

mixin Counter {
  int count = 0;
  void increment() {
    count++;
    print("Count: $count");
  }
}

class CounterService with Counter {}

void main() {
  CounterService service = CounterService();
  service.increment(); // 输出: Count: 1
  service.increment(); // 输出: Count: 2
}

7. 使用注意事项

  1. mixin 不能有构造函数。
  2. mixin 不能直接被实例化,必须与 class 结合使用。
  3. on 限定 mixin 只能应用于指定的基类。
  4. abstract 类不能直接实例化,必须由子类实现抽象方法。
  5. with 允许在类中混入多个 mixin,但顺序可能影响方法解析。
  6. mixin 允许包含 late 字段或带默认值的字段,但不允许非 late 且无默认值的字段。

mixin 组合使用示例

mixin A {
  void method() => print("A");
}

mixin B {
  void method() => print("B");
}

class C with A, B {}

void main() {
  C c = C();
  c.method(); // 输出: B (B 覆盖了 A 的 method)
}

这说明 Dart 采用后来的 mixin 优先的解析规则。

8. 结论

Dart 通过 mixinabstractonwith 关键字提供了一种强大的代码复用机制。合理使用这些关键字可以提高代码的灵活性和可维护性。