Skip to content

Instantly share code, notes, and snippets.

@talamaska
Last active November 27, 2019 13:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save talamaska/c728833a8859614c6e1e0c40b8e3f361 to your computer and use it in GitHub Desktop.
Save talamaska/c728833a8859614c6e1e0c40b8e3f361 to your computer and use it in GitHub Desktop.
Constructors in Dart
class Robot {
Robot();
}
// or if no arguments
class Robot {
}
// normal instanciation
var robot = Robot();
class Robot {
double height;
Robot(height) : this.height = height;
}
class Robot {
double height;
Robot(data) : this.height = data.physics.raw['heightInFt'];
}
//short cut
class Robot {
double height;
Robot(this.height);
}
// instanciation with param
var r = Robot(5);
// instanciation with param that is an Object like
var r = Robot(myData);
class Robot {
static mToFt(m) => m * 3.281;
double height; // in ft
Robot(height) : this.height = mToFt(height);
}
// extending
class Machine {
String name;
Machine(this.name);
}
class Robot extends Machine {
static mToFt(m) => m * 3.281;
double height;
Robot(height, name) : this.height = mToFt(height), super(name);
}
// adding guards
class Robot {
final double height;
Robot(height) : this.height = height, assert(height > 4.2);
}
// simple example
class Robot {
double height;
Robot(this.height);
}
main() {
var r = Robot(5);
print(r.height); // 5
}
// you can reassign value to the height
main() {
var r = Robot(5);
r.height = 6;
print(r.height); // 6
}
// that way you make the param private and expose a public getter
class Robot {
double _height;
Robot(this._height);
get height {
return this._height;
}
}
// or simpler
class Robot {
double _height;
Robot(this._height);
get height => _height;
}
// full equivalend of the first robot
class Robot {
double _height;
Robot(this._height);
get height => _height;
set height(value) => _height = value;
}
// not possible to use setters as param to the constructor
class Robot {
double _height;
Robot(this.height); // ERROR: 'height' isn't a field in the enclosing class
get height => _height;
set height(value) => _height = value;
}
// use the setter in the constructor body
class Robot {
double _height;
Robot(h) {
height = h;
}
get height => _height;
set height(value) => _height = value;
}
// named arguments
class Robot {
final double height;
final double weight;
final List<String> names;
Robot({ this.height, this.weight, this.names });
}
main() {
final r = Robot(height: 5, names: ["Walter"]);
print(r.height); // 5
}
// adding guards to named arguments constructors
class Robot {
final double height;
final double weight;
final List<String> names;
Robot({ this.height, @required this.weight, this.names });
}
// or
class Robot {
final double height;
final double weight;
final List<String> names;
Robot({ this.height, this.weight, this.names }) : assert(weight != null);
}
// cannot make positional params private
class Robot {
final double _height;
final double _weight;
final List<String> _names;
Robot({ this._height, this._weight, this._names }); // ERROR: Named optional parameters can't start with an underscore
}
// this works
class Robot {
final double _height;
final double _weight;
final List<String> _names;
Robot({ height, weight, names }) : _height = height, _weight = weight, _names = names;
get height => _height;
get weight => _weight;
get names => _names;
}
// or even with default values
class Robot {
final double _height;
final double _weight;
final List<String> _names;
Robot({ height, weight, names }) : _height = height ?? 7, _weight = weight, _names = names;
get height => _height;
get weight => _weight;
get names => _names;
}
main() {
print(Robot().height); // 7
}
// using public keys looks better
class Robot {
final double height;
final double weight;
final List<String> names;
Robot({ this.height = 7, this.weight = 100, this.names = const [] });
}
main() {
print(Robot().weight); // 100
}
// Both positional and named argument styles can be used together:
class Robot {
final double _height;
final double _weight;
final List<String> _names;
Robot(height, { weight, names }) :
_height = height,
_weight = weight,
_names = names;
get height => _height;
get weight => _weight;
}
main() {
var r = Robot(7, weight: 120);
print(r.height); // 7
print(r.weight); // 120
}
// Positional optional parameters
// Wrap the optional parameter with [ ] square brackets.
class User {
String name;
int age;
String home;
User(this.name, this.age, [this.home = 'Earth']);
}
User user1 = User('Bob', 34);
User user2 = User('Bob', 34, 'Mars');
// Named optional parameters
// Wrap the optional parameter with { } curly braces.
class User {
String name;
int age;
String home;
User(this.name, this.age, {this.home = 'Earth'});
}
User user1 = User('Bob', 34);
User user2 = User('Bob', 34, home: 'Mars');
// Note
// If you need private fields then you can use [] square brackets:
class User {
int _id;
User([this._id]);
}
User user = User(3);
// or do as the accepted answer says and use an initializer list:
class User {
int _id;
User({int id})
: _id = id;
}
User user = User(id: 3);
// that's why we have named constructors
class Robot {
final double height;
Robot(this.height);
Robot.fromPlanet(String planet) : height = (planet == 'geonosis') ? 2 : 7;
Robot.copy(Robot other) : this(other.height);
}
// What happened in copy? We used this to call the default constructor, effectively "redirecting" the instantiation.
main() {
print(Robot.copy(Robot(7)).height); // 7
print(Robot.fromPlanet('geonosis').height); // 2
print(Robot.fromPlanet('earth').height); // 7
}
// with inheritance
class Machine {
String name;
Machine();
Machine.named(this.name);
}
class Robot extends Machine {
final double height;
Robot(this.height);
Robot.named({ height, name }) : this.height = height, super.named(name);
}
main() {
print(Robot.named(height: 7, name: "Walter").name); // Walter
}
// you may want to keep private the default constructor and expose only named ones
// We can make a constructor private by prefixing it with an underscore:
class Robot {
Robot._();
}
// you can call a private constructor from a named one
class Machine {
String name;
Machine._();
Machine.named(this.name);
}
class Robot extends Machine {
final double height;
Robot._(this.height, name) : super.named(name);
Robot.named({ height, name }) : this._(height, name);
}
main() {
print(Robot.named(height: 7, name: "Walter").name); // Walter
}
// Factory constructors are syntactic sugar for the “factory pattern”, usually implemented with static functions.
// They appear like a constructor from the outside (useful for example to avoid breaking API contracts),
// but internally they can delegate instance creation invoking a “normal” constructor.
// This explains why factory constructors do not have initializers.
// Since factory constructors can return other instances (so long as they satisfy the interface of the current class),
// we can do very useful things like:
// * caching: conditionally returning existing objects (they might be expensive to create)
// * subclasses: returning other instances such as subclasses
// They work with both normal and named constructors!
class Robot {
final double height;
Robot._(this.height);
factory Robot() {
return Robot._(7);
}
}
main() {
print(Robot().height); // 7
}
// Here’s our robot warehouse, that only supplies one robot per height:
class Robot {
final double height;
static final _cache = <double, Robot>{};
Robot._(this.height);
factory Robot(height) {
return _cache[height] ??= Robot._(height);
}
}
main() {
final r1 = Robot(7);
final r2 = Robot(7);
final r3 = Robot(9);
print(r1.height); // 7
print(r2.height); // 7
print(identical(r1, r2)); // true
print(r3.height); // 9
print(identical(r2, r3)); // false
}
// Finally, to demonstrate how a factory would instantiate subclasses,
// let’s create different robot brands that calculate prices as a function of height:
abstract class Robot {
factory Robot(String brand) {
if (brand == 'fanuc') return Fanuc(2);
if (brand == 'yaskawa') return Yaskawa(9);
if (brand == 'abb') return ABB(7);
throw "no brand found";
}
double get price;
}
class Fanuc implements Robot {
final double height;
Fanuc(this.height);
double get price => height * 2922.21;
}
class Yaskawa implements Robot {
final double height;
Yaskawa(this.height);
double get price => height * 1315 + 8992;
}
class ABB implements Robot {
final double height;
ABB(this.height);
double get price => height * 2900 - 7000;
}
main() {
try {
print(Robot('fanuc').price); // 5844.42
print(Robot('abb').price); // 13300
print(Robot('flutter').price);
} catch (err) {
print(err); // no brand found
}
}
The factory constructor Robot(height) simply always returns the one and only instance that was created
when loading the Robot class.
class Robot {
static final Robot _instance = Robot._(7);
final double height;
factory Robot() {
return _instance;
}
Robot._(this.height);
}
main() {
var r1 = Robot();
var r2 = Robot();
print(identical(r1, r2)); // true
print(r1 == r2); // true
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment