9. Classes¶
Class declarations introduce new reference types and describe the manner of their implementation.
Classes can be top-level and local (see Local Classes and Interfaces).
A class body contains declarations and class initializers.
Declarations can introduce class members (see Class Members) or class constructors (see Constructor Declaration).
The body of the declaration of a member comprises the scope of a declaration (see Scopes).
Class members include:
Fields,
Methods, and
Accessors.
Class members can be declared or inherited.
Every member is associated with the class declaration it is declared in.
Field, method, accessor and constructor declarations can have the following access modifiers (see Access Modifiers):
Public,
Protected,
Internal, or
Private.
A newly declared field can hide a field declared in a superclass or superinterface.
A newly declared method can hide, implement, or override a method declared in a superclass or superinterface.
Every class defines two scopes (see Scopes): one for instance members, and the other for static members. This means that two members of a class can have the same name if one is static while the other is not.
The body of the declaration of a method (see Method Declarations) comprises the scope of a declaration (see Scopes).
9.1. Class Declarations¶
Every class declaration defines a Class type, i.e., a new named reference type.
The class name is specified by the identifier inside a class declaration.
If typeParameters are defined in a class declaration, then that class is a generic class (see Generic Declarations).
classDeclaration:
classModifier? 'class' identifier typeParameters?
classExtendsClause? implementsClause? classBody
;
classModifier:
'abstract' | 'final'
;
The scope of a class declaration is specified in Scopes.
An example of a class is presented below:
1 class Point {
2 public x: number
3 public y: number
4 public constructor(x : number, y : number) {
5 this.x = x
6 this.y = y
7 }
8 public distanceBetween(other: Point): number {
9 return Math.sqrt(
10 (this.x - other.x) * (this.x - other.x) +
11 (this.y - other.y) * (this.y - other.y)
12 )
13 }
14 static origin = new Point(0, 0)
15 }
9.1.1. Abstract Classes¶
A class with the modifier abstract
is known as abstract class.
Abstract classes can be used to represent notions that are common
to some set of more concrete notions.
A compile-time error occurs if an attempt is made to create an instance of an abstract class:
1abstract class X {
2 field: number
3 constructor (p: number) { this.field = p }
4}
5let x = new X (666)
6 // Compile-time error: Cannot create an instance of an abstract class.
Subclasses of an abstract class can be non-abstract or in turn abstract. A non-abstract subclass of an abstract superclass can be instantiated. As a result, a constructor for the abstract class, and field initializers for non-static fields of that class are executed.
1abstract class Base {
2 field: number
3 constructor (p: number) { this.field = p }
4}
5
6class Derived extends Base {
7 constructor (p: number) { super(p) }
8}
A method with the modifier abstract
is considered an abstract method
(see Abstract Methods).
Abstract methods do not have bodies, i.e., they can be declared but not
implemented.
Only abstract classes can have abstract methods. A compile-time error occurs if a non-abstract class has an abstract method:
1class Y {
2 abstract method (p: string): void
3 /* Compile-time error: Abstract methods can only
4 be within an abstract class. */
5}
A compile-time error occurs if an abstract method declaration
contains the modifiers final
or override
.
9.1.2. Final Classes¶
Final classes are described in the chapter Experimental Features (see Final Classes).
9.1.3. Class Extension Clause¶
All classes except class Object can contain the extends clause that specifies the base class, or the direct superclass of the current class. A class that has no extends clause, and is not Object, is assumed to have the extends Object clause.
The class that inherits from its superclass is called subclass of that superclass.
classExtendsClause:
'extends' typeReference
;
A compile-time error occurs if:
An extends clause appears in the definition of the class Object, which is the top of the type hierarchy, and has no superclass.
The class type named by typeReference is not accessible (see Scopes).
The ‘extends’ graph has a cycle.
typeReference refers directly, or as alias of types primitive, enum, union, interface, or function.
Any type argument of typeReference is a wildcard type argument.
Class extension implies that a class inherits all members of the direct superclass. Notice that private members are also inherited from the superclass but they are not accessible within the subclass.
1 class Base {
2 // All methods are mutually accessible in the class where
3 they were declared
4 public publicMethod () {
5 this.protectedMethod()
6 this.privateMethod()
7 }
8 protected protectedMethod () {
9 this.publicMethod()
10 this.privateMethod()
11 }
12 private privateMethod () {
13 this.publicMethod();
14 this.protectedMethod()
15 }
16 }
17 class Derived extends Base {
18 foo () {
19 this.publicMethod() // OK
20 this.protectedMethod() // OK
21 this.privateMethod() // compile-time error:
22 // the private method is inaccessible
23 }
24 }
The transitive closure of a direct subclass relationship is the subclass relationship. Class A can be a subclass of class C if:
A is the direct subclass of C; or
A is a subclass of some class B, which is in turn a subclass of C (i.e., the definition applies recursively).
Class C is a superclass of class A if A is its subclass.
9.1.4. Class Implementation Clause¶
A class can implement one or more interfaces. Interfaces that are to be implemented by a class are listed in the implements clause. Interfaces listed in this clause are direct superinterfaces of the class.
implementsClause:
'implements' interfaceTypeList
;
interfaceTypeList:
typeReference (',' typeReference)*
;
A compile-time error occurs if:
typeReference fails to name an accessible interface type (see Scopes).
Any type argument of typeReference is a wildcard type argument.
An interface is repeated as a direct superinterface in a single implements clause (even if that interface is named differently).
For the class declaration C <F1,…, Fn> (\(n\geq{}0\), \(C\neq{}Object\)):
Direct superinterfaces of the class type C <F1,…, Fn> are the types specified in the implements clause of the declaration of C (if there is an implements clause).
For the generic class declaration C <F1,…, Fn> (n > 0):
Direct superinterfaces of the parameterized class type C < T1,…, Tn> are all types I <U1\(\theta{}\),…, Uk\(\theta{}\)> if:
Ti (\(1\leq{}i\leq{}n\)) is a type;
I <U1,…, Uk> is the direct superinterface of C <F1,…, Fn>; and
\(\theta{}\) is the substitution [F1:= T1,…, Fn:= Tn].
Interface type I is a superinterface of class type C if I is one of the following:
Direct superinterface of C;
Superinterface of J which is in turn a direct superinterface of C (see Superinterfaces and Subinterfaces that defines superinterface of an interface); or
Superinterface of the direct superclass of C.
A class implements all its superinterfaces.
A compile-time error occurs if a class is at the same time a subtype of:
Two interface types that represent different instantiations of the same generic interface (see Generic Declarations); or
The instantiation of a generic interface, and a raw type that names the a generic interface.
If a class is not declared abstract, then:
Any abstract method of each direct superinterface is implemented (see Overriding by Instance Methods) by a declaration in that class.
The declaration of the existing method is inherited from a direct superclass, or a direct superinterface.
If a default method (see Default Method Declarations) of a superinterface is not inherited, then that default method can:
Be overridden by a class method; and
Behave as specified in its default body.
A single method declaration in a class is allowed to implement methods of one or more superinterfaces.
A compile-time error occurs if a class field and a method from one of superinterfaces that a class implements have the same name, except when one is static and the other is not.
9.1.5. Implementing Interface Properties¶
A class must implement all properties from all interfaces (see Implementing Interface Properties) which are defined as a getter, a setter, or both. Providing implementation for the property in the form of a field is not necessary.
1 interface Style {
2 get color(): string
3 set color(s: string)
4 }
5
6 class StyleClassOne implements Style {
7 color: string = ""
8 }
9
10 class StyleClassTwo implements Style {
11 private color_: string = ""
12
13 get color(): string {
14 return this.color_
15 }
16
17 set color(s: string) {
18 this.color_ = s
19 }
20 }
9.2. Class Body¶
A class body can contain declarations of the following members:
Fields,
Methods,
Accessors,
Constructors, and
Static initializers for the class.
classBody:
'{'
classBodyDeclaration* classInitializer? classBodyDeclaration*
'}'
;
classBodyDeclaration:
accessModifier?
( constructorDeclaration
| classFieldDeclaration
| classMethodDeclaration
| classAccessorDeclaration
)
;
Declarations can be inherited or immediately declared in a class. Any declaration within a class has a class scope. The class scope is fully defined in Scopes.
9.3. Class Members¶
Class members are as follows:
Members inherited from their direct superclass (see Inheritance), except class Object that cannot have a direct superclass.
Members declared in a direct superinterface (see Superinterfaces and Subinterfaces).
Members declared in the class body (see Class Body).
Class members declared private are not accessible to all subclasses of the class.
Class members declared protected or public are inherited by subclasses that are declared in a package other than the package containing the class declaration.
Constructors and class initializers are not members, and are not inherited.
Members can be as follows:
Class fields (see Field Declarations),
Methods (see Method Declarations), and
Accessors (see Accessor Declarations).
A method is defined by the following:
Type parameter, i.e., the declaration of any type parameter of the method member.
Argument type, i.e., the list of types of arguments applicable to the method member.
Return type, i.e., the return type of the method member.
A throws/rethrows clause, i.e., an indication of the ability of a member method to raise exceptions.
Members can be as follows:
Static members that are not part of class instances, and can be accessed by using a qualified name notation (see Names) anywhere the class name or the interface name is accessible; and
Non-static, or instance members that belong to any instance of the class.
All names in both static and non-static class declaration scopes (see Scopes) must be unique, i.e., fields and methods cannot have the same name.
9.4. Access Modifiers¶
Access modifiers define how a class member or a constructor can be accessed. Accessibility in ArkTS can be of the following kinds:
private,
internal,
protected, or
public.
The desired accessibility of class members and constructors can be explicitly specified by the corresponding access modifiers:
accessModifier:
'private'
| 'internal'
| 'protected'
| 'public'
;
If no explicit modifier is provided, then a class member or a constructor is implicitly considered public by default.
9.4.1. Private Access Modifier¶
The modifier private
indicates that a class member or a constructor is
accessible within its declaring class, i.e., a private member or
constructor m declared in a class C can be accessed only within the
class body of C:
1 class C {
2 private count: number
3 getCount(): number {
4 return this.count // ok
5 }
6 }
7
8 function increment(c: C) {
9 c.count++ // compile-time error – 'count' is private
10 }
9.4.2. Internal Access Modifier¶
The modifier internal
is described in the chapter Experimental Features
(see Internal Access Modifier).
9.4.3. Protected Access Modifier¶
The modifier protected
indicates that a class member or a constructor is
accessible only within its declaring class and the classes derived from that
declaring class. A protected member M declared in a class C can be
accessed only within the class body of C or of a class derived from C:
1 class C {
2 protected count: number
3 getCount(): number {
4 return this.count // ok
5 }
6 }
7
8 class D extends C {
9 increment() {
10 this.count++ // ok, D is derived from C
11 }
12 }
13
14 function increment(c: C) {
15 c.count++ // compile-time error – 'count' is not accessible
16 }
9.4.4. Public Access Modifier¶
The modifier public
indicates that a class member or a constructor can be
accessed everywhere, provided that the member or the constructor belongs to
a type that is also accessible.
9.5. Field Declarations¶
Field declarations represent data members in class instances.
classFieldDeclaration:
fieldModifier* variableDeclaration
;
fieldModifier:
'static' | 'readonly'
;
A compile-time error occurs if:
The same field modifier is used more than once in a field declaration.
The name of a field declared in the body of a class declaration is already used for another field or method in the same declaration.
A field declared by a class with a certain name hides any accessible declaration of fields if they have the same name in superclasses and superinterfaces of the class.
If a hidden field is static, then it can be accessed with the qualification
of a superclass or of a superinterface, or with a field access expression with
the keyword super
(see Field Access Expressions).
A class can access all non-private fields of a superclass and superinterfaces from its direct superclass and direct superinterfaces if such non-private fields are both:
Accessible (see Scopes) to code in the class; and
Not hidden by a declaration in the class.
A class can inherit more than one field or property with the same name from its superinterfaces, or from both its superclass and superinterfaces. However, an attempt to refer to such a field or property by its simple name within the body of the class causes a compile-time error.
The same field or property declaration can be inherited from an interface in more than one way. In that case, the field or property is considered to be inherited only once (and thus, referring to it by its simple name causes no ambiguity).
9.5.1. Static Fields¶
There are two categories of class or interface fields:
Class, or static fields
Static fields are declared with the static
modifier. They are not parts
of class instances. There is one copy of a static field, irrespective of how
many instances of that class (even if zero) are eventually created.
Static fields are accessed using a qualified name notation (where the class or interface name is used as a qualifier, see Names) in any place where the class or interface name is accessible.
Instance, or non-static fields.
Instance fields belong to each instance of the class. An instance field is created for, and associated with a newly-created instance of a class or its superclass. Instance fields are accessible via the name of the instance.
9.5.2. Readonly (Constant) Fields¶
A field that has the modifier readonly
is a readonly field. Changing
the value of a readonly field after initialization is not allowed.
Both static and non-static fields can be declared readonly fields.
A static readonly field must be initialized as follows:
By using a field initializer, or
As a result of a class initializer (see Class Initializer).
Otherwise, a compile-time error occurs.
A non-static readonly field must be initialized as a result of execution of any class constructor. Otherwise, a compile-time error occurs.
9.5.3. Field Initialization¶
An initializer in a non-static field declaration has the semantics of an assignment (see Assignment) to the declared variable.
The following rules apply to an initializer in a static field declaration:
A compile-time error occurs if the initializer uses the keywords
this
orsuper
while calling a method (see Method Call Expression) or accessing a field (see Field Access Expressions).The initializer is evaluated, and the assignment is performed only once when the class is initialized at runtime.
Note: Constant fields are initialized before all other static fields.
Constant fields initialization never uses default values (see Default Values for Types).
In a non-static field declaration, an initializer is evaluated at runtime. Its assignment is performed each time an instance of the class is created. The initializer can use the following keywords:
this
to access the current object methods or fields, or to refer to the current object;super
to access the methods declared in a superclass;
Additional restrictions (as specified in Exceptions and Errors Inside Field Initializers) apply to variable initializers that refer to fields which cannot be initialized yet.
References to a field (even if the field is in the scope) can be restricted. The rules applying to the restrictions on forward references to fields (if the reference textually precedes the field declaration) and self-references (if the field is used within its own initializer) are provided below.
A compile-time error occurs in a reference to a static field f declared in class or interface C if:
Such a reference is used in C’s static initializer (see Class Initializer) or static field initializer (see Field Initialization);
Such a reference is used before the declaration of f, or within f’s own declaration initializer;
No such reference is present on the left-hand side of an assignment expression (see Assignment);
C is the innermost class or interface that encloses such a reference.
A compile-time error occurs in a reference to a non-static field f declared in class C if such reference is:
Used in the non-static field initializer of C;
Used before the declaration of f, or within f’s own declaration initializer;
Not present on the left-hand side of an assignment expression (see Assignment);
Enclosed in C that is the innermost class or interface.
9.6. Method Declarations¶
Methods declare executable code that can be called:
classMethodDeclaration:
methodOverloadSignature*
methodModifier* typeParameters? identifier signature block?
;
methodModifier:
'abstract'
| 'static'
| 'final'
| 'override'
| 'native'
;
Overloading signature of a method allows calling a method in different ways.
The identifier of classMethodDeclaration is the method name that can be used to refer to a method (see Method Call Expression).
A compile-time error occurs if:
A method modifier appears more than once in a method declaration.
The body of a class declaration declares a method but the name of that method is already used for a field in the same declaration.
The body of a class declaration declares two same-name methods with override-equivalent signatures (see Override-Equivalent Signatures) as members of that body of a class declaration.
9.6.1. Class (Static) Methods¶
A method declared with the modifier static
is a class method.
Another name for class methods is static methods.
A compile-time error occurs if:
A method declaration contains another modifier (
abstract
,final
, oroverride
) along with the modifierstatic
.The header or body of a class method includes the name of a type parameter of the surrounding declaration.
Class methods are always called without reference to a particular object. As
a result, a compile-time error occurs if keywords this
or super
are used inside a static method.
9.6.2. Instance Methods¶
A method that is not declared static is called non-static method, or an instance method.
An instance method is always called with respect to an object that becomes
the current object the keyword this
refers to during the execution
of the method body.
9.6.3. Abstract Methods¶
An abstract method declaration introduces the method as a member along
with its signature but without an implementation. An abstract method is
declared with the modifier abstract
in its declaration.
Non-abstract methods can be referred to as concrete methods.
A compile-time error occurs if:
An abstract method is declared private.
A method declaration contains another modifier (
static
,final
, ornative
) along with the modifierabstract
.The abstract method m declaration does not appear directly within an abstract class A.
Any non-abstract subclass of A (see Abstract Classes) does not provide an implementation for m.
An abstract method can be overridden by another abstract method declaration provided by an abstract subclass.
A compile-time error occurs if an abstract method overrides a non-abstract instance method.
9.6.4. Final Methods¶
Final methods are described in the chapter Experimental Features (see Native Methods).
9.6.5. Overriding Methods¶
The override
modifier indicates that an instance method in a superclass is
overridden by the corresponding instance method from a subclass (see
Overriding by Instance Methods).
The usage of the modifier override
is optional but strongly recommended as
it makes the overriding explicit.
A compile-time error occurs if:
A method marked with the modifier
override
does not override a method from a superclass.A method declaration that contains the modifier
override
also contains the modifiersabstract
orstatic
.
If the signature of the overridden method contains parameters with default values (see Optional Parameters), then the overriding method always uses the default parameter values of the overridden method.
A compile-time error occurs if a parameter in the overriding method has a default value.
See Overriding by Instance Methods for the specific rules of overriding.
9.6.6. Native Methods¶
Native methods are described in the chapter Experimental Features (see Native Methods).
9.6.7. Method Overload Signatures¶
ArkTS allows specifying a method that has several overload signatures, i.e., several method headers that have the same name followed by one implementation body.
methodOverloadSignature:
methodModifier* identifier signature
;
A compile-time error occurs if the method implementation is not present, or does not immediately follow the declaration.
A call of a method with overload signatures is always a call of the implementation method.
The example below has one overload signature parameterless; the other two have one parameter each:
1 class C {
2 foo(): void // 1st signature
3 foo(x: string): void // 2nd signature
4 foo(x?: string): void // implementation signature
5 {
6 console.log(x)
7 }
8 }
9 let c = new C()
10 c.foo() // ok, call fits 1st and 3rd signatures
11 c.foo("aa") // ok, call fits 2nd and 3rd signatures
12 c.foo(undefined) // ok, call fits the 3rd signature
The call c.foo()
is executed as a call of the implementation method with
the undefined
argument. The call c.foo(x)
is executed as a call of the
implementation method with an argument.
Overload signature compatibility requirements are described in Overload Signature Compatibility.
In addition, a compile-time error occurs if not all of the following requirements are met:
Overload signatures and the implementation method have the same access modifier.
All overload signatures and the implementation method are static or non-static.
All overload signatures and the implementation method are final or non-final.
Overload signatures are not native (however, native implementation method is allowed).
Overload signatures are not abstract.
9.6.8. Method Body¶
A method body is a block of code that implements a method. A semicolon, or an empty body (i.e., no body at all) indicate the absence of the implementation.
An abstract or native method must have an empty body.
In particular, a compile-time error occurs if:
The body of an abstract or native method declaration is a block.
A method declaration is neither abstract nor native, but its body is empty, or is a semicolon.
See return Statements for the rules that apply to return statements in a method body.
A compile-time error occurs if a method is declared to have a return type, but its body can complete normally (see Normal and Abrupt Statement Execution).
9.6.9. Inheritance¶
Class C inherits from its direct superclass all concrete methods m (both static and instance) that meet all of the following requirements:
m is a member of the direct superclass of C;
m is public, protected, or internal in the same package as C;
No signature of a method declared in C is compatible with the signature of m (see Compatible Signature).
Class C inherits from its direct superclass and direct superinterfaces all abstract and default methods m (see Default Method Declarations) that meet the following requirements:
m is a member of D, which is a direct superclass or direct superinterface of C;
m is public, protected, or internal in the same package as C;
No method declared in C has a signature that is compatible with the signature of m (see Compatible Signature);
No signature of a concrete method inherited by C from its direct superclass is compatible with the signature of m (see Compatible Signature);
No method \(m'\) that is a member of D’, which is a direct superclass or direct superinterface of C (while \(m'\) is distinct from m, and \(D'\) from D), overrides the declaration of the method m from \(D'\) (see Overriding by Instance Methods for class method overriding, and Overriding by Instance Methods for interface method overriding).
No class can inherit private or static methods from its superinterfaces.
9.6.10. Overriding by Instance Methods¶
The instance method mC (inherited by, or declared in class C) overrides another method mA (declared in class A) if all the following requirements are met:
C is a subclass of A;
C does not inherit mA;
The signature of mC is compatible with the signature of mA (see Compatible Signature);
—and if one of the following is also true:
mA is public;
mA is protected; or
mA is internal in the same package as C while:
Either C declares mC; or
mA is a member of the direct superclass of C;
mA is declared with package access, and mC overrides:
mA from a superclass of C; or
method \(m'\) from C (while \(m'\) is distinct from both mC and mA, i.e., \(m'\) overrides mA from a superclass of C).
Non-abstract mC implements mA from C if it overrides an abstract method mA.
An instance method mC (inherited by, or declared in class C) overrides another method mI (declared in interface I) from C if all of the following requirements are met:
I is a superinterface of C;
mI is not static;
C does not inherit mI;
The signature of mC is a subsignature of the signature of mI (see Override-Equivalent Signatures); and
mI is public.
A method call expression (see Method Call Expression) containing the
keyword super
can be used to call an overridden method.
Among the methods that override each other, return types can vary if they are reference types.
The specialization of return type to a subtype (i.e., covariant returns) is based on the concept of return-type-substitutability. For example, the method declaration d1 with return type R1 is return-type-substitutable for another method d2 with return type R2 if:
R1 is a primitive type (R2 is identical to R1); or
R1 is a reference type (R1 adapted to type parameters of d2 is a subtype of R2).
9.6.11. Hiding by Class Methods¶
A static method m declared in, or inherited by a class C hides any method \(m'\) (where the signature of m is a subsignature of the signature of \(m'\) as described in Override-Equivalent Signatures) in its superclasses and superinterfaces.
A hidden method is not directly accessible (see Scopes) to code in C.
However, a hidden method can be accessed by using a qualified name, or a method
call expression (see Method Call Expression) that contains the keyword
super
or a cast to a superclass type.
A compile-time error occurs if a static method hides an instance method.
9.6.12. Requirements in Overriding and Hiding¶
The method declaration d1 with return type R1 can override or hide the declaration of another method d2 with return type R2 if d1 is return-type-substitutable for d2 (see Requirements in Overriding and Hiding and Overriding by Instance Methods). Otherwise, a compile-time error occurs.
A method that overrides or hides another method (including the methods that
implement abstract methods defined in interfaces) cannot change ‘throws
’ or
‘rethrows
’ clauses of the overridden or hidden method.
A compile-time error occurs if a type declaration T has a member method m1, but there is also a method m2 declared in T or a supertype of T, for which all of the following requirements are met:
m1 and m2 use the same name; and
m2 is accessible from T (see Scopes); and
The signature of m1 is not a subsignature of m2 (see Override-Equivalent Signatures).
The access modifier of an overriding or hiding method must provide no less access than was provided in the overridden or hidden method.
A compile-time error occurs if:
The overridden or hidden method is public, and the overriding or hiding method is not public.
The overridden or hidden method is protected, and the overriding or hiding method is not protected or public.
The overridden or hidden method has internal access, and the overriding or hiding method is private.
9.6.13. Inheriting Methods with Override-Equivalent Signatures¶
A class can inherit multiple methods with override-equivalent signatures (see Override-Equivalent Signatures).
A compile-time error occurs if a class C inherits the following:
Concrete method whose signature is override-equivalent to another method that C inherited; or
Default method whose signature is override-equivalent to another method that C inherited, if there is no abstract method, declared in a superclass of C and inherited by C, that is override-equivalent to both methods.
An abstract class can inherit all the methods, assuming that a set of override-equivalent methods consists of at least one abstract method, and zero or more default methods.
A compile-time error occurs if one of the inherited methods is not
return-type-substitutable for every other inherited method (except ‘throws
’
and ‘rethrows
’ clauses that cause no error in this case).
The same method declaration can be inherited from an interface in a number of ways, causing no compile-time error on its own.
9.7. Accessor Declarations¶
Accessors are often used instead of fields to add additional control for operations of getting or setting a field value. An accessor can be either a getter or a setter.
classAccessorDeclaration:
accessorModifier
( 'get' identifier '(' ')' returnType block?
| 'set' identifier '(' parameter ')' block?
)
;
accessorModifier:
'abstract'
| 'static'
| 'final'
| 'override'
;
Accessor modifiers are a subset of method modifiers. The allowed accessor
modifiers have exactly the same meaning as the corresponding method modifiers.
See Abstract Methods for the modifier abstract
,
Class (Static) Methods for the modifier static
, Final Methods
for the modifier final
, and Overriding Methods for the modifier
override
.
1 class Person {
2 private _age: number = 0
3 get age(): number { return this._age }
4 set age(a: number) {
5 if (a < 0) { throw new Error("wrong age") }
6 this._age = a
7 }
8 }
Each get-accessor (getter) must have neither parameters nor an explicit return type. Each set-accessor (setter) must have a single parameter and no return value. The use of getters and setters looks the same as the use of fields:
1 class Person {
2 private _age: number = 0
3 get age(): number { return this._age }
4 set age(a: number) {
5 if (a < 0) { throw new Error("wrong age") }
6 this._age = a
7 }
8 }
9
10 let p = new Person()
11 p.age = 25 // setter is called
12 if (p.age > 30) { // getter is called
13 // do something
14 }
A class can define a getter, a setter, or both with the same name. If both a getter and a setter with a particular name are defined, then both must have the same accessor modifiers. Otherwise, a compile-time error occurs.
Accessors can be implemented by using a private field to store its value (as in the example above).
1 class Person {
2 name: string = ""
3 surname: string = ""
4 get fullName(): string {
5 return this.surname + " " + this.name
6 }
7 }
9.8. Class Initializer¶
When a class is initialized, the class initializer declared in the class is executed along with all class initializers of all superclasses. The order of execution is from the top superclass to the current class. Class initializers (along with field initializers for static fields as described in Field Initialization) ensure that all static fields receive their initial values before the first use.
1 classInitializer
2 : 'static' block
3 ;
A compile-time error occurs if a class initializer contains:
A
return <expression>
statement (see return Statements).A
throw
statement (see throw Statements) with no surroundingtry
statement (see try Statements) to handle the error or exception.Keywords
this
(see this Expression) orsuper
(see Method Call Expression and Field Access Expressions), or any type of a variable declared outside the class initializer.
Restrictions of class initializers’ ability to refer to static fields (even those within the scope) are specified in Exceptions and Errors Inside Field Initializers. Class initializers cannot throw exceptions as they are effectively non-throwing functions (see Non-Throwing Functions).
9.9. Constructor Declaration¶
Constructors are used to initialize objects that are instances of class.
A constructor declaration starts with the keyword constructor
, and has no
name. In any other syntactical aspect, a constructor declaration is similar to
a method declaration with no return type.
constructorDeclaration:
constructorOverloadSignature*
'constructor' '(' parameterList? ')' throwMark? constructorBody
;
throwMark:
'throws'
| 'rethrows'
;
Constructors are called by the following:
Class instance creation expressions (see New Expressions);
Conversions and concatenations caused by the string concatenation operator ‘\(+\)’ (see String Concatenation); and
Explicit constructor calls from other constructors (see Constructor Body).
Access to constructors is governed by access modifiers (see Access Modifiers and Scopes). Declaring a constructor inaccessible can prevent class instantiation.
A compile-time error occurs if two constructors in a class are declared, and have identical signatures.
See Throwing Functions for ‘throws
’ mark, and
Rethrowing Functions for ‘rethrows
’ mark.
9.9.1. Formal Parameters¶
The syntax and semantics of a constructor’s formal parameters are identical to those of a method.
9.9.2. Constructor Overload Signatures¶
ArkTS allows specifying a constructor that can be called in different ways by providing overload signatures, i.e., several constructor headers which are followed by one constructor implementation body.
constructorOverloadSignature:
accessModifier? 'constructor' signature
;
A compile-time error occurs if the constructor implementation is not present, or does not immediately follow the declaration.
A call of a constructor with overload signature is always a call of the constructor implementation body.
The example below has one overload signature parameterless, and others have one parameter each:
1 class C {
2 constructor() // 1st signature
3 constructor(x: string) // 2nd signature
4 constructor(x?: string) // 3rd - implementation signature
5 {
6 console.log(x)
7 }
8 }
9 new C() // ok, fits the 1st and 3rd signatures
10 new C("aa") // ok, fits the 2nd and 3rd signatures
11 new C(undefined) // ok, fits 3rd signature
The new expression (see New Expressions) new C()
leads to a call of
the constructor implementation with the argument undefined
. The new C(x)
creates an object calling constructor implementation with ‘x’ as an argument.
Overload signature compatibility requirements are described in Overload Signature Compatibility.
A compile-time error occurs if at least two different overload signatures or implementation signatures have different access modifiers.
1 class Incorrect {
2 // Constructors have different access modifiers
3 private constructor() // private 1st signature
4 protected constructor(x: string) // protected 2nd signature
5 constructor(x?: string) // public 3rd - implementation signature
6 {}
7 }
9.9.3. Constructor Body¶
The first statement in a constructor body can be an explicit call of another same-class constructor, or of the direct superclass (see Explicit Constructor Call):
constructorBody:
'{' constructorCall? statement* '}'
;
constructorCall:
'this' arguments
| 'super' arguments
;
1 class Point {
2 x: number
3 y: number
4 constructor(x: number, y: number) {
5 this.x = x
6 this.y = y
7 }
8 }
9
10 class ColoredPoint extends Point {
11 static readonly WHITE = 0
12 static readonly BLACK = 1
13 color: number
14 constructor(x: number, y: number, color: number) {
15 super(x, y) // calls base class constructor
16 this.color = color
17 }
18 }
A compile-time error occurs if a constructor calls itself, directly or
indirectly, through a series of one or more explicit constructor calls
using this
.
The constructor body must implicitly begin with a superclass constructor
call ‘super()
’ (call of the constructor’s direct superclass that takes
no argument), if the constructor body does not begin with an explicit
constructor call. The constructor so declared is a part of the primordial
class Object.
A constructor body looks like a method body (see Method Body), except that explicit constructor calls are possible, and explicit return of a value (see return Statements) is prohibited. However, a return statement without an expression can be used in a constructor body.
A constructor body must not use fields of a created object before the fields are initialized; this cannot be passed as an argument until each object field receives an initial value. The check can be performed by the compiler that reports a compile-time error if a violation is detected. In difficult corner cases checks must be performed at runtime. The check raises an exception if an attempt to work with a non-initialized object field is detected.
9.9.4. Explicit Constructor Call¶
There are two kinds of explicit constructor call statements:
Alternate constructor calls that begin with the keyword
this
, and can be prefixed with explicit type arguments (used to call an alternate same-class constructor).Superclass constructor calls (used to call a constructor from the direct superclass) called unqualified superclass constructor calls that begin with the keyword
super
, and can be prefixed with explicit type arguments.
A compile-time error occurs if the constructor body of an explicit constructor call statement:
Refers to any non-static field or instance method; or
Uses the keywords
this
orsuper
in any expression.
An ordinary method call evaluates an alternate constructor call statement left-to-right. The evaluation starts from arguments, proceeds to constructor, and then the constructor is called.
The process of evaluation of a superclass constructor call statement is performed as follows:
If instance i is created, then the following procedure is used to determine i’s immediately enclosing instance with respect to S (if available):
If the declaration of S occurs in a static context, then i has no immediately enclosing instance with respect to S.
If the superclass constructor call is unqualified, then S must be a local class.
If S is a local class, then the immediately enclosing type declaration of S is O.
If n is an integer (\(n\geq{}1\)), and O is the n’th lexically enclosing type declaration of C, then i’s immediately enclosing instance with respect to S is the n’th lexically enclosing instance of
this
.
After i’s immediately enclosing instance with respect to S (if available) is determined, the evaluation of the superclass constructor call statement continues left-to-right. The arguments to the constructor are evaluated, and then the constructor is called.
If the superclass constructor call statement completes normally after all, then all non-static field initializers of C are executed. I is executed before J if a non-static field initializer I textually precedes another non-static field initializer J.
Non-static field initializers are executed if the superclass constructor call:
Has an explicit constructor call statement; or
Is implicit.
An alternate constructor call does not perform the implicit execution.
9.9.5. Default Constructor¶
If a class contains no constructor declaration, then a default constructor is implicitly declared. The default constructor has the following form:
The access modifier of the default constructor and of the class is the same (if the class has no access modifier, then the default constructor has the internal access (see Scopes).
The default constructor has no ‘
throws
’ or ‘rethrows
’ clauses.The body of the default constructor contains a call to the the superclass constructor with no arguments except the primordial class
Object
. The body of the default constructor for the primordial classObject
is empty.
A compile-time error occurs if a default constructor is implicit, but the superclass:
Has no accessible constructor without parameters; and
Has a constructor without parameters but with ‘
throws
’ or ‘rethrows
’ clauses.
1// Class declarations without constructors
2class Object {}
3class Base {}
4class Derived extends Base {}
5
6
7// Class declarations with default constructors declared implicitly
8class Object {
9 constructor () {} // Empty body - as there is no supercalss
10}
11// Default constructors added
12class Base { constructor () { super () } }
13class Derived extends Base { constructor () { super () } }
14
15// Example of an error case #1
16class A {
17 private constructor () {}
18}
19class B extends A {} // No constructor in B
20// During compialtion of B
21class B extends A { constructor () { super () } } // Default constructor added
22// And it leads to conpile-time error as default constructor calls super()
23// which is private and inaccessible
24
25// Example of an error case #2
26class A {
27 constructor () throws {}
28}
29class B extends A {} // No constructor in B
30// During compialtion of B
31class B extends A { constructor () { super () } } // Default constructor added
32// And it leads to conpile-time error as default constructor is not marked as throws
33// but it call super() which throws
9.10. Local Classes and Interfaces¶
Local classes and interfaces (see Interfaces) are declared within the body of a function, method, or any block delimited by balanced braces in a group of zero or more statements.
Names of local classes and interfaces are visible only within the scope they are declared in. When declared in a scope, names of local classes and interfaces have access to entities visible within this scope, and capture the entities they use from this scope. Function/method parameters and local variables can be used and thus captured.
A compile-time error occurs if:
A local class or interface declaration has access modifier
public
,protected
, orprivate
.A local class or interface declaration members have access modifier
public
,protected
,private
, orexport
.
The example below shows local classes and interfaces in a top-level function:
1 function foo (parameter: number) {
2 let local: string = "function local"
3 interface LocalInterface { // Local interface in a top-level function
4 method (): void // It has a method
5 field: string // and a property
6 }
7 class LocalClass implements LocalInterface { // Class implements interface
8 // Local class in a top-level function
9 method () { console.log ("Instance field = ", this.field, " par = ", parameter, " loc = ", local) }
10 field: string = "`instance field value`"
11 static method () { console.log ("Static field = ", LocalClass.field) }
12 static field: string = "`class/static field value`"
13 }
14 let lc: LocalInterface = new LocalClass
15 // Both local types can be freely used in the top-level function scope
16 lc.method()
17 LocalClass.method()
18 }
The example below shows local classes and interfaces in a class method. The algorithm is similar to that in a top-level function. However, the surrounding class members are not accessible from local classes:
1 class A_class {
2 field: number = 1234 // Not visible for the local class
3 method (parameter: number) {
4 let local: string = "instance local"
5 interface LocalInterface {
6 method (): void
7 field: string
8 }
9 class LocalClass implements LocalInterface {
10 method () { console.log ("Instance field = ", this.field, " par = ", parameter, " loc = ", local) }
11 field: string = "`instance field value`"
12 static method () { console.log ("Static field = ", LocalClass.field) }
13 static field: string = "`class/static field value`"
14 }
15 let lc: LocalInterface = new LocalClass
16 lc.method()
17 LocalClass.method()
18 }
19 static method (parameter: number) {
20 let local: string = "class/static local"
21 interface LocalInterface {
22 method (): void
23 field: string
24 }
25 class LocalClass implements LocalInterface {
26 method () { console.log ("Instance field = ", this.field, " par = ", parameter, " loc = ", local) }
27 field: string = "`instance field value`"
28 static method () { console.log ("Static field = ", LocalClass.field) }
29 static field: string = "`class/static field value`"
30 }
31 let lc: LocalInterface = new LocalClass
32 lc.method()
33 LocalClass.method()
34 }
35 }