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.
- 将字段声明、参数声明和初始化合并为单个声明,即声明式参数声明。
- 支持在非延迟字段初始化器中安全引用构造函数参数(主初始化作用域)。
- 允许使用分号()简洁表示空声明体。
; - 为体内构造函数引入简洁缩写语法。
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);要声明主构造函数,需在类型名称(及可选类型参数)后直接添加参数列表:
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.
主构造函数参数列表区分三种参数类型:
- 声明式参数:由或
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 常量主构造函数
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);
}要将主构造函数设为,需在声明头部的类/类型名称前添加关键字:
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 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
扩展类型必须使用主构造函数。
- 头部中的单个参数是表示字段。
- 表示变量不能使用修饰符(使用
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 主构造函数的体内部分(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 {...}如果主构造函数需要断言或自定义字段初始化,可在体中使用语法声明:
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 主初始化作用域
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;
}当声明主构造函数时,其形参被引入主初始化作用域。此作用域是类体中非延迟字段初始化器和主构造函数初始化列表(之后)的当前作用域。
这允许非延迟字段在声明期间直接引用构造函数参数:
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 延迟实例变量限制
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
主初始化作用域对实例变量初始化器不生效。
late- 由于变量可在构造完成后求值,其初始化器无法安全访问构造函数参数。
late - 尝试在字段初始化器中访问主构造函数参数会导致编译时错误。
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
在主初始化作用域内,主构造函数参数会遮蔽同名的类成员(字段):
- 在非延迟初始化器中:引用参数
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.
为保证主构造函数(及关联的初始化作用域)始终执行:
- 带有主构造函数的类、mixin类或枚举声明不能声明任何其他非重定向生成构造函数(扩展类型除外)。
- 体内声明的所有其他生成构造函数必须(直接或间接)重定向到主构造函数。
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 :
在初始化阶段,主构造函数参数不可赋值。
- 在字段初始化器或初始化列表中对参数进行任何赋值(例如
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 | 在 | 将字段改为非延迟,或通过另一个非延迟字段传递值。 |
| 在字段声明和 | 删除其中一个初始化操作。 |
| 在体内声明了非重定向的生成构造函数,未重定向到主构造函数。 | 修改体内构造函数使其重定向(例如 |
5. Step-by-Step Refactoring Workflows
5. 分步重构流程
Workflow 5.1: Migrating a Class to a Primary Constructor
流程5.1:将类迁移到主构造函数
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 }
按照以下步骤将冗长的类迁移到新的主构造函数语法:
-
识别候选字段和构造函数: 定位生成构造函数及其初始化的字段。在此示例中,是和
name字段。agedart// Before class User { final String name; final int age; User(this.name, this.age); } -
将字段移至头部: 将字段添加到头部并使用或
final修饰符,如果体为空则追加分号(var)。现在;和name字段作为声明式参数age和final String name写入主构造函数。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); } -
利用主初始化作用域进行计算: 如果字段值由参数计算得出,在体内声明该字段并直接使用参数赋值: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; } -
将体内构造函数转换为重定向构造函数: 确保所有体内生成构造函数都重定向到主构造函数: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
}