In addition to object attributes, a class definition may also contain 'shared' data, which is shared by all the objects of that class.
Shared attributes are similar to object attributes, but are shared between all the instances of a class. They are essentially global variables that reside within a class namespace. They can be accessed and modified by any instance of the class. Shareds can have the same private and readonly restrictions that regular attributes have
private shared i,j:INT; readonly shared c:CHAR := 'x' |
Unlike regular attributes, when only a single shared attribute is defined, a constant initializing expression may be provided.
shared s:STR := "name"; -- ILLEGAL shared s,p:STR := "name"; -- cannot use initializing expression if two shareds are -- declared at the same time |
If no initializing expression is provided, the shared is initialized to the value 'void'.
Constants are accessible by all objects in a class and may not be assigned to - they must have an initializing expression from which their value is determined at compile time (there is an exception when no type is specified, as descrbed in the next subsection). If a type is specified, then the construct defines a single constant attribute which must be initialized to a constant expression. Constant expressions are recursively composed out of a combination of literals, function calls on literals, and references to other constants. More precisely, legal assignments are to
a character, boolean, string, integer or floating point literal
a void or void test expression
an and or or expression, each of whose components is a constant expression
an array literal, each of whose components is a constant expression
a routine call applied to a constant expression, each of whose arguments is a constant expression other than void. This caveat is imporant, since create routines are called on void. Thus the following is illegal:[1]
-- ILLEGAL const a:POINT := #POINT(3,3); const a:POINT := void; -- The only legal kind of constant POINT is void |
[1] Implementation Note: The compiler currently does not always detect this illegal case.
a reference to another constant in the same class or in another class using the '::' notation.
const r:FLT:=45.6; -- Reader routine is private r:FLT; private const a,b,c; private const d:=4,e,f const bar:BOOL := r > 10; -- Function call on constants const foo:ARRAY{INT} := |1,2,4,5,6|; -- Sather arrays are explained later const baz ::= BAR::foz ; -- foz must be a constant expression in foz |
[1] Implementation Note: The compiler currently does not always detect this illegal case.
If a type specifier is not provided, then no initializing expression is required and the construct defines one or more successive integer constants. The first identifier is assigned the value zero by default; its value may also be specified by a constant expression of type INT. The remaining identifiers are assigned successive integer values. This is the way to do enumeration types in Sather. It is an error if no type is specified and there is an assignment that is not of type INT.
const a; -- a is of type INT and gets the value 0 const c,d; -- c gets 0 and d gets 1 const e := 3; -- e is also of type INT |
There must not be cyclic dependencies among constant initializers.
class FOO is const b:INT := BAR::c; class BAR is const c:INT := BAZ::d; class BAZ is -- ILLEGAL! const d:INT := FOO::b; -- Introduces a cycle between b, c and d |
Since constant initialization involves permits operations on the built-in types, the operations on the built-in types are designed so that no observable side-effects can occur during constant initialization.
The prefix readonly cannot be applied to constants, since constants cannot be modified in any case.
Due to their definition, constants are only useful for the basic classes such as numbers, booleans and characters. All other constants can only be assigned to be void!
class FOO is const a:BAR := void; -- only legal value |
It is possible to directly access the class data or features using the :: notation.
class FOO is const a:INT := 3; private const b:INT := 5; readonly shared c:INT := 6; shared d:INT := 7; attr f:INT; create(i:INT):SAME is res:SAME := new; res.f := i; return res; end; method1:INT is return d+a; end; method2:INT is return f+a; end; end; |
The shared and const class data can then be accessed using the :: notation
#OUT+ FOO::a+"\n"; FOO::d := 3; |
When a method is called using the '::' notation, it is equivalent to calling the method on a void object. Calling a method on a void object makes sense if the feature only makes use of shared data and local state. If the method makes use of object data, a run-time error will result.
#OUT+FOO::method1; -- Prints out d+a = 10 #OUT+FOO::method2; -- Tries to print out self.f+a -- However, self (the object) is void, so trying to access 'f' -- results in a run-time error - Attribute access of void |
The usual privacy and modification restrictions are maintained
a_copy:INT := FOO::a; -- ILLEGAL FOO::c :=3; -- c is readonly -- FOO::a := 7; -- a is a constant |