Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions examples/misc/lib/language_tour/classes/point_alt.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,42 @@ class PointD {
}

// #enddocregion initialize-null

// #docregion initialize-private-named
class PointPrivate {
final double? _x; // Nullable field
final double _y; // Non-nullable field

PointPrivate({this._x, this._y = 0.0});

@override
String toString() => 'PointPrivate($_x, $_y)';
}

void testPrivate() {
var p = PointPrivate(x: 1.0, y: 2.0);
print(p);
}
// #enddocregion initialize-private-named

// #docregion initialize-private-named-assert
class PointPrivateAssert {
final double _x;

PointPrivateAssert({required this._x}) : assert(_x >= 0);
}
// #enddocregion initialize-private-named-assert

// #docregion initialize-private-named-super
class Tool {
final int _price;
Tool({required this._price});
}

class Hammer extends Tool {
// Forwards to the public 'price' argument
Comment thread
conooi marked this conversation as resolved.
Hammer({required super.price});
}
// #enddocregion initialize-private-named-super


10 changes: 10 additions & 0 deletions examples/misc/lib/language_tour/classes/point_private_new.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// #docregion initialize-private-named-after
class Point {
final double _x;
Point({required this._x});
}
// #enddocregion initialize-private-named-after

// #docregion initialize-private-named-usage
var p = Point(x: 1.0);
// #enddocregion initialize-private-named-usage
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// #docregion initialize-private-named-before
class Point {
final double _x;
Point({required double x}) : _x = x;
}

// #enddocregion initialize-private-named-before
2 changes: 1 addition & 1 deletion examples/misc/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ description: dart.dev example code.

resolution: workspace
environment:
sdk: ^3.11.0
sdk: ^3.12.0-0

dependencies:
args: ^2.7.0
Expand Down
142 changes: 123 additions & 19 deletions src/content/language/constructors.md
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,10 @@ For more discussion, watch this Decoding Flutter video on tear-offs.

## Instance variable initialization

Dart can initialize variables in three ways.
Dart provides several ways to initialize instance variables.
You can assign values in the declaration,
use initializing formal parameters,
or use an initializer list.

### Initialize instance variables in the declaration

Expand Down Expand Up @@ -326,24 +329,6 @@ class PointB {
}
```

Private fields can't be used as named initializing formals.

{% comment %}
Don't attach the following example to a code excerpt.
It doesn't work on purpose and will cause errors in CI.
{% endcomment %}

```dart
class PointB {
// ...

PointB.namedPrivate({required double x, required double y})
: _x = x,
_y = y;

// ...
}
```

This also works with named variables.

Expand Down Expand Up @@ -393,6 +378,125 @@ class PointD {
}
```

### Private named parameters
Comment thread
parlough marked this conversation as resolved.

:::version-note
Using private named parameters as initializing formals
requires a [language version][] of at least 3.12.
:::

In Dart, fields starting with an underscore are private to their library.
To initialize a private field using a named parameter,
you can write manual assignment boilerplate
in the initializer list:

<?code-excerpt "point_private_old.dart (initialize-private-named-before)" plaster="none"?>
```dart
class Point {
final double _x;
Point({required double x}) : _x = x;
}
```

You can also initialize private fields directly
in the constructor parameter list.
When you prefix the named parameter with `this._`,
the compiler automatically strips the underscore for the caller,
allowing them to use a clean, public name:

<?code-excerpt "point_private_new.dart (initialize-private-named-after)" plaster="none"?>
```dart
class Point {
final double _x;
Point({required this._x});
}
```

In both cases,
the caller uses the public name `x` at the call site:

<?code-excerpt "point_private_new.dart (initialize-private-named-usage)" plaster="none"?>
```dart
var p = Point(x: 1.0);
```

Like regular [named parameters](/language/functions#named-parameters), you can
make private named parameters optional or required.
You can also provide explicit default values.

In the following example, the `_x` parameter is optional and defaults to `null`.
The `_y` parameter is also optional but has an explicit default value of `0.0`:

<?code-excerpt "point_alt.dart (initialize-private-named)" plaster="none"?>
```dart
class PointPrivate {
final double? _x; // Nullable field
final double _y; // Non-nullable field

PointPrivate({this._x, this._y = 0.0});

@override
String toString() => 'PointPrivate($_x, $_y)';
}

void testPrivate() {
var p = PointPrivate(x: 1.0, y: 2.0);
print(p);
}
```
Comment thread
conooi marked this conversation as resolved.

#### Constraints

* **No conflicts:** Neither the private name nor the generated public name
can match any other parameter name in the same constructor.
* **Initializing formals only:** Named parameters in Dart generally
can't be private. This capability is an exception that only applies
to named parameters that are initializing formals (`this._field`).
Comment thread
conooi marked this conversation as resolved.
You can't use private identifiers for regular named parameters.
Comment on lines +452 to +455

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The description of this constraint is slightly incomplete. The private named parameters feature applies to both initializing formals (this._field) and super-initializer parameters (super._field). It would be more accurate to include both in the explanation.

Suggested change
* **Initializing formals only:** Named parameters in Dart generally
can't be private. This capability is an exception that only applies
to named parameters that are initializing formals (`this._field`).
You can't use private identifiers for regular named parameters.
* **Initializing formals and super parameters only:** Named parameters in Dart
generally can't be private. This capability is an exception that only applies
to named parameters that are initializing formals (this._field)
or super-initializer parameters (super._field).
You can't use private identifiers for regular named parameters.

* **Valid public name:** The private name must map to a valid public identifier.
Comment thread
conooi marked this conversation as resolved.
For example, `this._` or `this._2x` are invalid
because they don't have valid public counterparts.

#### Usage in initializer lists

Within the constructor's initializer list,
reference the parameter using its private name:

<?code-excerpt "point_alt.dart (initialize-private-named-assert)" plaster="none"?>
```dart
class PointPrivateAssert {
final double _x;

PointPrivateAssert({required this._x}) : assert(_x >= 0);
}
```

#### Interaction with super parameters

When extending a class that uses private named parameters,
subclasses use the public name for [super parameters][].

In the following example, the `Tool` class defines the private field `_price`.
Even though the field is private,
its corresponding named parameter is public (`price` not `_price`).
To pass the value along,
the `Hammer` subclass uses the public `price` identifier:

<?code-excerpt "point_alt.dart (initialize-private-named-super)" plaster="none"?>
```dart
class Tool {
final int _price;
Tool({required this._price});
}

class Hammer extends Tool {
// Forwards to the public 'price' argument
Hammer({required super.price});
}
```

[super parameters]: /resources/glossary#super-parameter

### Use an initializer list

Before the constructor body runs, you can initialize instance variables.
Expand Down
27 changes: 27 additions & 0 deletions src/data/glossary.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1921,6 +1921,33 @@
- "subtyping"
- "subtype polymorphism"

- term: "Super parameter"
Comment thread
conooi marked this conversation as resolved.
Comment thread
conooi marked this conversation as resolved.
short_description: |-
A constructor parameter that automatically passes
an argument to the superclass constructor.
long_description: |-
A **super parameter** is a syntax shorthand that simplifies
passing parameters from a subclass constructor
up to a superclass constructor.

Instead of manually declaring the parameter
and passing it in the initializer list,
you can prefix the parameter name with `super.`.

For example, in `Hammer({required super.price})`,
the `price` value is passed
along directly to the superclass constructor.
related_links:
- text: "Super parameters"
link: "/language/constructors#super-parameters"
type: "doc"
labels:
- "language"
- "constructors"
- "syntax"
alternate:
- "Super-initializer parameter"

- term: "Transitive dependency"
short_description: |-
A dependency that a package indirectly uses because
Expand Down
Loading