dart-use-primary-constructors
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDart Primary Constructors & New Constructor Syntax Skill
Dart Primary Constructors及新构造函数语法技能
Use this skill when helping users write, refactor, or debug code using Dart's Primary Constructors feature.
当帮助用户使用Dart的Primary Constructors特性编写、重构或调试代码时,可使用本技能。
Dart Version Requirements
Dart版本要求
- Dart 3.13 and above: Primary constructors are enabled by default.
- Dart 3.12: The feature is available but experimental. Users must explicitly enable the experiment flag via
primary-constructorsor in--enable-experiment=primary-constructors:analysis_options.yaml
yaml
analyzer:
enable-experiment:
- primary-constructors- Dart 3.11 and earlier: Primary constructors are not supported.
- Dart 3.13及以上:Primary Constructors默认启用。
- Dart 3.12:该特性可用但处于实验阶段。用户必须通过或在
--enable-experiment=primary-constructors中显式启用实验标志analysis_options.yaml:primary-constructors
yaml
analyzer:
enable-experiment:
- primary-constructors- Dart 3.11及更早版本:不支持Primary Constructors。
1. Overview
1. 概述
Primary Constructors allow developers to declare a non-redirecting generative constructor as well as a set of instance variables directly in the class header. This significantly reduces boilerplate and improves code readability.
Primary Constructors允许开发者在类头中直接声明一个非重定向生成构造函数以及一组实例变量。这大幅减少了样板代码并提升了代码可读性。
Key Benefits
核心优势
- Combines field declaration, parameter declaration, and initialization into a single declaration known as a declaring parameter declaration.
- Enables safe reference to constructor parameters in non-late field initializers (Primary Initializer Scope).
- Allows empty declaration bodies to be represented concisely with a semicolon ().
; - Introduces abbreviated concise syntax for in-body constructors.
- 将字段声明、参数声明和初始化合并为单个声明(称为声明式参数声明)。
- 支持在非late字段初始化器中安全引用构造函数参数(Primary Initializer Scope)。
- 允许使用分号()简洁表示空声明体。
; - 为体内构造函数引入了简洁缩写语法。
2. Syntax Reference
2. 语法参考
2.1 Basic Class Header Syntax
2.1 基础类头语法
To declare a primary constructor, place a parameter list immediately after the type name (and optional type parameters):
dart
// Declares fields x and y, and a generative constructor Point(this.x, this.y)
class Point(var int x, var int y);
// Declares final fields
class PointFinal(final int x, final int y);要声明Primary Constructor,需在类型名称(及可选类型参数)后紧跟参数列表:
dart
// Declares fields x and y, and a generative constructor Point(this.x, this.y)
class Point(var int x, var int y);
// Declares final fields
class PointFinal(final int x, final int y);2.2 Declaring, Initializing, and Plain Parameters
2.2 声明式、初始化式与普通参数
A primary constructor parameter list distinguishes between three types of parameters:
- Declaring Parameters: Indicated by the or
varmodifier (e.g.,final). They implicitly create a corresponding instance field in the class.final int x - Initializing Parameters: Indicated by the or
this.prefix (e.g.,super.orthis.x). They initialize an existing field or a super constructor parameter, respectively.super.x - Regular Parameters: Declared without modifiers (e.g., ). They do not become fields and are only available during initialization (e.g., in field initializers or the
int yinitializer list in the class body).this :
dart
// `x` is a field and a parameter because it has the keyword `final`. In particular, we can use the name `x` in the initializer list in the in-body part of the primary constructor. 'y' is a only parameter because it has neither of the keywords `final` or `var`, but `y` is passed to the super constructor via the `this :` initializer list.
class C(final int x, int y) extends Base {
this : super(y);
}Declaring parameters and initializing parameters are two ways of achieving the same goal: declaring a class with instance fields which are set in the constructor. Regular parameters are different in that their values are not automatically routed to an instance field.
Primary Constructor的参数列表区分三种参数类型:
- 声明式参数:由或
var修饰符标识(例如final)。它们会在类中隐式创建对应的实例字段。final int x - 初始化式参数:由或
this.前缀标识(例如super.或this.x)。它们分别用于初始化现有字段或父类构造函数参数。super.x - 普通参数:无修饰符声明(例如)。它们不会成为字段,仅在初始化阶段可用(例如字段初始化器或类体中的
int y初始化列表)。this :
dart
// `x` is a field and a parameter because it has the keyword `final`. In particular, we can use the name `x` in the initializer list in the in-body part of the primary constructor. 'y' is a only parameter because it has neither of the keywords `final` or `var`, but `y` is passed to the super constructor via the `this :` initializer list.
class C(final int x, int y) extends Base {
this : super(y);
}声明式参数和初始化式参数是实现同一目标的两种方式:声明一个在构造函数中设置实例字段的类。普通参数则不同,它们的值不会自动路由到实例字段。
2.3 Constant Primary Constructors
2.3 常量Primary Constructor
To make a primary constructor , place the keyword before the class/type name in the declaration header:
constconstdart
class const Point(final int x, final int y);
extension type const Ext(int x);
enum const MyEnum(final int x) {
entry(1);
}要将Primary Constructor设为,需在声明头中的类/类型名称前添加关键字:
constconstdart
class const Point(final int x, final int y);
extension type const Ext(int x);
enum const MyEnum(final int x) {
entry(1);
}2.4 Extension Types
2.4 扩展类型(Extension Types)
Extension types must use primary constructors.
- The single parameter in the header is the representation field.
- The representation variable cannot use the modifier (using
vartriggers thevarerror).representation_field_modifier - The representation variable can optionally use the modifier. If
finalis not present then it is inferred; that is, the parameter is declaring whether or not it's explicitlyfinal.final
扩展类型必须使用Primary Constructor。
- 头中的单个参数是表示字段。
- 表示变量不能使用修饰符(使用
var会触发var错误)。representation_field_modifier - 表示变量可选择性使用修饰符。如果未指定
final,则会自动推断;即参数会明确声明是否为final。final
2.5 Empty Body Semicolon Shorthand (;
)
;2.5 空体分号简写(;
)
;When a class, mixin class, mixin, extension or extension type has an empty body, the braces can be replaced by a semicolon ():
{};dart
class C(int x);
mixin class MC;
extension type ET(int x);
mixin M;
extension Ext on C;当类、mixin类、mixin、扩展或扩展类型的体为空时,大括号可替换为分号():
{};dart
class C(int x);
mixin class MC;
extension type ET(int x);
mixin M;
extension Ext on C;2.6 The In-Body Part of a Primary Constructor (this ...
)
this ...2.6 Primary Constructor的体内部分(this ...
)
this ...If a primary constructor requires assertions or custom field initializations, they can be declared in the body using the syntax:
this :dart
class Point(var int x, var int y) {
// Initializer list in class body
this : assert(x >= 0), y = y * 2;
}You can also write a constructor body with this syntax ().
this {...}如果Primary Constructor需要断言或自定义字段初始化,可使用语法在体中声明:
this :dart
class Point(var int x, var int y) {
// Initializer list in class body
this : assert(x >= 0), y = y * 2;
}你也可以使用此语法编写构造函数体()。
this {...}2.7 Abbreviated Concise Constructor Syntax
2.7 简洁缩写构造函数语法
For constructors declared within the class body, the class name can be omitted and replaced with the or keywords:
newfactory| Traditional Syntax | Abbreviated Concise Syntax |
|---|---|
| |
| |
| |
| |
| |
| |
对于在类体中声明的构造函数,可省略类名,替换为或关键字:
newfactory| 传统语法 | 简洁缩写语法 |
|---|---|
| |
| |
| |
| |
| |
| |
3. Semantics & Scoping Rules
3. 语义与作用域规则
3.1 Primary Initializer Scope
3.1 Primary初始化作用域
When a primary constructor is declared, its formal parameters are introduced into the Primary Initializer Scope. This scope is the current scope for non-late field initializers in the class body and the primary constructor's initializer list (after ).
This allows non-late fields to reference constructor parameters directly during declaration:
this :dart
class DeltaPoint(final int x, int delta) {
// 'x' and 'delta' are in scope here
final int y = x + delta;
}当声明Primary Constructor时,其形参被引入Primary Initializer Scope。该作用域是类体中非late字段初始化器以及Primary Constructor初始化列表(之后)的当前作用域。
这允许非late字段在声明期间直接引用构造函数参数:
this :dart
class DeltaPoint(final int x, int delta) {
// 'x' and 'delta' are in scope here
final int y = x + delta;
}3.2 Late Instance Variables Restriction
3.2 Late实例变量限制
The primary initializer scope is not active for instance variable initializers.
late- Since variables can be evaluated after construction has completed, their initializers cannot safely access constructor parameters.
late - Attempting to access a primary constructor parameter in a field initializer results in a compile-time error.
late
Primary初始化作用域对实例变量初始化器不生效。
late- 由于变量可在构造完成后求值,其初始化器无法安全访问构造函数参数。
late - 尝试在字段初始化器中访问Primary Constructor参数会导致编译时错误。
late
3.3 Shadowing
3.3 遮蔽
Primary constructor parameters shadow class members (fields) of the same name within the primary initializer scope:
- In a non-late initializer: refers to parameter
int y = x.x - In a initializer:
laterefers to fieldlate int y = x(if it exists) because the parameterxis out of scope.x
在Primary初始化作用域内,Primary Constructor参数会遮蔽同名的类成员(字段):
- 在非late初始化器中:引用的是参数
int y = x。x - 在初始化器中:
late引用的是字段late int y = x(如果存在),因为参数x超出了作用域。x
3.4 Generative Constructor Restrictions
3.4 生成构造函数限制
To guarantee that the primary constructor (and the associated initializer scope) always executes:
- A class, mixin class, or enum declaration with a primary constructor cannot declare any other non-redirecting generative constructors (except extension types).
- All other generative constructors declared in the body must redirect (directly or indirectly) to the primary constructor.
为确保Primary Constructor(及关联的初始化作用域)始终执行:
- 带有Primary Constructor的类、mixin类或枚举声明不能声明任何其他非重定向生成构造函数(扩展类型除外)。
- 体中声明的所有其他生成构造函数必须(直接或间接)重定向到Primary Constructor。
3.5 Parameter Mutation Errors
3.5 参数修改错误
Primary constructor parameters are non-assignable inside the initialization phase.
- Any assignment to a parameter (e.g., ,
p = value) inside field initializers or thep++initializer list is a compile-time error.this :
在初始化阶段,Primary Constructor参数不可赋值。
- 在字段初始化器或初始化列表中对参数进行任何赋值操作(例如
this :、p = value)都会导致编译时错误。p++
3.6 Double Initialization Errors
3.6 重复初始化错误
Initializing a field twice (e.g., once in the field declaration/initializer and once in the initializer list or as an initializing formal) is a compile-time error.
this :对字段进行两次初始化(例如一次在字段声明/初始化器中,一次在初始化列表或作为初始化式形参)会导致编译时错误。
this :4. Diagnostics & Troubleshooting
4. 诊断与故障排除
Most errors and lints have quick-fixes, run to fix those violations. For other common errors, fix them using the following table:
dart fix| Error / Lint Code | Common Cause | Resolution |
|---|---|---|
| Invalid Late Access | Referencing a primary constructor parameter inside a | Make the field non-late, or pass the value through another non-late field. |
| Initializing a variable both in its declaration and in the | Remove one of the initializations. |
| Declaring a in-body generative constructor in the body without redirecting to the primary. | Change the in-body constructor such that it is redirecting (e.g. |
大多数错误和提示都有快速修复方案,运行即可修复这些问题。对于其他常见错误,可使用下表进行修复:
dart fix| 错误/提示代码 | 常见原因 | 解决方法 |
|---|---|---|
| Invalid Late Access | 在 | 将字段设为非late,或通过另一个非late字段传递值。 |
| 同时在声明和 | 删除其中一个初始化操作。 |
| 在体中声明了非重定向到Primary Constructor的体内生成构造函数。 | 修改体内构造函数使其重定向(例如 |
5. Step-by-Step Refactoring Workflows
5. 分步重构流程
Workflow 5.1: Migrating a Class to a Primary Constructor
流程5.1:将类迁移至Primary Constructor
Follow these steps to migrate a verbose class to the new primary constructor syntax:
-
Identify Candidate Fields and Constructor: Locate generative constructors and the fields they initialize. In this case, this would be theand
namefields.agedart// Before class User { final String name; final int age; User(this.name, this.age); } -
Move Fields to the Header: Place fields in the header withor
finalmodifiers and append a semicolon (var) if the body is empty. The;andnamefields are now written the primary constructor as declaring parametersageandfinal String name, respectively.final int agedart// After class User(final String name, final int age); -
Handle Custom Initializers and Assertions: If there is an initializer list or assert block, move it to ablock inside the body:
thisdart// Before class Point { final int x; final int y; Point(this.x, this.y) : assert(x >= 0); } // After class Point(final int x, final int y) { this : assert(x >= 0); } -
Leverage Primary Initializer Scope for Calculations: If a field value is calculated from parameters, declare it inside the body and assign it directly using the parameters:dart
// Before class Rect { final double width; final double height; final double area; Rect(this.width, this.height) : area = width * height; } // After class Rect(final double width, final double height) { // 'width' and 'height' are in scope here final double area = width * height; } -
Convert In-Body Constructors to Redirecting: Ensure all in-body generative constructors redirect to the primary constructor:dart
// Before class Point { final int x; final int y; Point(this.x, this.y); Point.zero() : x = 0, y = 0; } // After class Point(final int x, final int y) { new zero() : this(0, 0); // Redirects to primary }
按照以下步骤将冗长的类迁移至新的Primary Constructor语法:
-
识别候选字段和构造函数: 定位生成构造函数及其初始化的字段。在本例中,候选字段是和
name。agedart// Before class User { final String name; final int age; User(this.name, this.age); } -
将字段移至类头: 将字段添加到类头中,使用或
final修饰符,如果体为空则追加分号(var)。;和name字段现在作为声明式参数age和final String name写入Primary Constructor。final int agedart// After class User(final String name, final int age); -
处理自定义初始化器和断言: 如果存在初始化列表或断言块,将其移至体中的块内:
thisdart// Before class Point { final int x; final int y; Point(this.x, this.y) : assert(x >= 0); } // After class Point(final int x, final int y) { this : assert(x >= 0); } -
利用Primary初始化作用域进行计算: 如果字段值由参数计算得出,在体中声明该字段并直接使用参数赋值:dart
// Before class Rect { final double width; final double height; final double area; Rect(this.width, this.height) : area = width * height; } // After class Rect(final double width, final double height) { // 'width' and 'height' are in scope here final double area = width * height; } -
将体内构造函数转换为重定向构造函数: 确保所有体内生成构造函数都重定向到Primary Constructor:dart
// Before class Point { final int x; final int y; Point(this.x, this.y); Point.zero() : x = 0, y = 0; } // After class Point(final int x, final int y) { new zero() : this(0, 0); // Redirects to primary }
Workflow 5.2: Applying Abbreviated (Concise) In-Body Constructors
流程5.2:应用简洁缩写体内构造函数
When the user prefers to keep the constructor in the class body but wants to reduce verbosity, suggest the abbreviated constructor syntax:
dart
// Before
class DatabaseService {
final String url;
DatabaseService(this.url);
DatabaseService.local() : url = 'localhost';
factory DatabaseService.create() => DatabaseService('default');
}
// After
class DatabaseService {
final String url;
new(this.url); // Omit class name, use 'new'
new local() : url = 'localhost'; // Use 'new local' for named constructors
factory create() => DatabaseService('default'); // Omit class name from factory
}当用户希望将构造函数保留在类体中但想减少冗余时,建议使用缩写构造函数语法:
dart
// Before
class DatabaseService {
final String url;
DatabaseService(this.url);
DatabaseService.local() : url = 'localhost';
factory DatabaseService.create() => DatabaseService('default');
}
// After
class DatabaseService {
final String url;
new(this.url); // Omit class name, use 'new'
new local() : url = 'localhost'; // Use 'new local' for named constructors
factory create() => DatabaseService('default'); // Omit class name from factory
}