Expressions
Construction
Creating objects by naming the type, choosing a constructor by label, supplying type arguments, and building a class that declares no constructor.
since 0.1.0-alpha.1linuxwindowswasm
Description
An object is created by naming its type and passing arguments. There is no new at the call site: new appears only in a constructor declaration.
Type(args) // construction
Type::Label(args) // the constructor declared with that label
Type::<int>(args) // explicit type arguments for a generic class
Type::Label::<int>(args) // explicit type arguments after the complete labeled callee
NS::Type(args) // a class in a namespace, reached by qualification
NS::Type::Label(args) // ...combined with a label
Base::Ctor(args) // inside a constructor: run a base-class constructor on this object
A constructor declaration new Label(...) { ... } gives the constructor a label that callers use to select it. Several constructors may share a label. The overload is chosen by the argument types, and between candidates that fit equally well the one declared first is used.
A bare declaration Type name; constructs the type's default value: "" for string, [] for an array, 0 for int.
Labeled constructors, defaults and namespaces
namespace Shapes {
class Circle {
int r;
new Unit() { r = 1; }
}
}
class Account {
string owner;
int balance;
new Open(string who, int start = 0) {
owner = who;
balance = start;
}
}
class Shape {
string name;
new Shape(string n) { name = n; }
}
class Square : Shape {
int side;
new Square(int s) {
Shape::Shape("square");
side = s;
}
}
Shapes::Circle c = Shapes::Circle::Unit();
console.writeln(c.r);
Account a = Account::Open("ann");
console.writeln("${a.owner} ${a.balance}");
Account b = Account::Open("bo", start: 9);
console.writeln("${b.owner} ${b.balance}");
Square q = Square(4);
console.writeln("${q.name} ${q.side}");
1
ann 0
bo 9
square 4
Rules
- Constructor arguments may be named, and constructor parameters may declare default values, with the same rules as any call (see
lang.calls). - The type arguments of a generic class are filled in this order: first a complete explicit
::<...>tuple, then the target type of the surrounding declaration or assignment, then the constructor arguments for any slot still open. SoBox<int> b = Box(1.9)constructs aBox<int>and converts the argument toint; the argument does not first fix the type tofloat. - A plain assignment's declared target works the same way as a declaration's: given
Map<string, int> m, bothMap<string, int> m = Map();and a laterm = Map();infer the key and value types fromm. - A generic construction whose type arguments have no source at all (no explicit tuple, no target type, no argument that carries the type) is a compile error. Write a target type, pass an argument that carries the type, or use an explicit tuple such as
Box::<int>(5). - The explicit tuple follows the complete callee:
Box::From::<int>(value), notBox::<int>::From(value). - Inside a constructor,
Base::Ctor(args)runs a base-class constructor onthis. The derived class decides when to call it.
A class without a constructor
A class that declares no constructor is still constructible. ClassName(a, b, ...) fills the data fields positionally, in declaration order. Giving fewer arguments than there are fields leaves the remaining fields at their defaults, and giving none is ordinary default construction. Giving more arguments than the class has data fields is a compile error: too many arguments to construct '...'.
The same rule explains why there are no casts. int(x) is not a conversion; it tries to construct an int, which has no fields, and fails with that error. Use x.toInt(), x.toFloat() or x.toString().
Construction without a constructor, and generic inference
class Email {
string address;
string label;
}
class Box<T> {
T value;
}
Email e = Email("a@b.c");
console.writeln("[${e.address}] [${e.label}]");
Box<int> truncated = Box(1.9);
console.writeln(truncated.value);
Box<float> kept = Box(1.9);
console.writeln(kept.value);
Box<int> explicit = Box::<int>(4);
console.writeln(explicit.value);
Map<string, int> m = Map();
m = Map();
m["k"] = 1;
console.writeln(m.length());
[a@b.c] []
1
1.900000
4
1
Examples
class Duo { int a; int b; }
Duo d = Duo(1, 2, 3);
// error: too many arguments to construct 'Duo': it has 2 assignable fields but 3 were given
class Box<T> { T value; }
Box b = Box();
// error: cannot infer type argument 'T' for 'Box'; provide a target type or a type-bearing argument
A labeled construction through the dot form
class Tagged {
int v;
new Of(int n) { v = n; }
}
console.writeln(Tagged::Of(9).v);
console.writeln(Tagged.Of(8).v);
9
8
Notes
- A local variable that has the same name as a class takes precedence over the class in a dot call: with
Tagged Tagged = Tagged::Of(5);in scope,Tagged.twice()calls the methodtwiceon the variable's value. The::spelling (Tagged::Of(9)) always names the class.
See also
- Map — An associative collection from keys of type
Kto values of typeV, with value semantics.