# class

Definition: | [global] [abstract] class identifier [nullable] [dynamic] [extends base] { [(public|private)[:]] ( field | method ) [ ',' ... ] } end class -- or -- class|identifier variable ( ';' | '=' ( new() | expr ) ) [ ',' ... ] -- or -- bool res = class|identifier(object o)
Description: | As above class can be used in three very distinct ways, almost identically to struct. Classes are superficially similar to structs, however in structs all fields are implicitly public whereas they are private by default in classes, structs cannot be fully dynamic, and typically classes have methods, while structs normally do not. (In general, Phix allows some reasonable overlap, rather than always strictly saying "NON" (in a cheesy French accent) even when there is no reasonable let alone outright compelling reason to do so. Which reasonably stands to reason, he reasoned reasonably.) An abstract class (or struct) cannot be instantiated, ie attempts to invoke new() trigger compile-time and/or run-time errors. By default struct and class variables cannot be set to null, unless the definition contains an explicit nullable clause. The dynamic and extends clauses are explained separately. Note that you should never pass a class reference to deep_copy(): doing so would almost guarantee being left with a reference to reclaimed memory or a reclaimed slot in a class instances table, hence the latter now [1.0.5+] has a guard in place to prevent such attempts. There are also some safety checks in builtins/structs.e that prohibit access to a shared reference to a class instance that has (somehow) already been through the delete() process, which would normally only occur when the reference count drops to zero, unless some other bit of code is trying to force the issue, such as by trying to manually destroy an instance-specific delegate, perhaps because setting it to null wasn’t doing it immediately, presumably because there was still some other reference to it when logically there should never be.
pwa/p2js: | Not supported.
Example: |
    class person
    --  private: -- (see note)
        string name = "average joe"
        public atom age = 35
        procedure show()
          printf(1,"%s, aged %g\n",{this.name,this.age})
        end procedure
    end class
    person p = new()
    --?p.name  -- error
    ?p.age    -- 35
    p.show() -- average joe, aged 35
If no default value is specified for a field, one will be provided automatically (0/false/""/{}/NULL). All fields in classes are private by default, whereas in structs they are implicitly public. All methods are public by default. Explicitly using private on a class field, or public on a method or in a struct, is exactly the same as omitting it/both. If public or private is followed by ':' it toggles/trashes that default behaviour (as well as applying to the next field/method). In the above, name is private by default, age is explicitly made public, whereas show is public by default: uncommenting that "private:" would not change name or age, but would make show() private. While initially fields and methods get different privacy defaults, both "private:" and "public:" affect them both, and there is no way to later revert to that initial difference. (You are of course encouraged to group fields and methods appropriately, to minimise the number of times public/private need to be typed in.) Class methods can use "this." to explicitly refer to class instance fields and methods. It can be omitted provided that doing so remains totally unambiguous, see technicalia.
Implementation: | As per struct
See Also: | extended, dynamic, javascript.this
Technicalia | Note that the lowest-level builtin type class() is actually an alias of, and behaves identically to, struct(): `?{struct(b),struct(p),class(b),class(p)}` yields {1,1,1,1}, ie all true, whereas `?{bridge(b),bridge(p),person(b),person(p)}` yields {1,0,0,1}, ie mixed true/false. Should you really need a struct/class distinction, not that I can think of any valid reason why you might, use `get_struct_type(s)=S_CLASS/S_STRUCT` instead. Class methods are automatically given a "this" first parameter, which is fulfilled by the object immediately to the left of the dot on the call. It should be possible to omit "this." inside method definitions, as long as doing so would otherwise cause a compilation error, for instance if you have say:
            class person
                atom age
                procedure set_age(atom age)
                    this.age := age
                end procedure
                procedure set_age2(atom a)
                    age := a
                end procedure
            end class
then in the set_age() procedure `age` and `this.age` genuinely refer to different things, and clearly omitting "this." would not cause a compilation error, but instead just copy the parameter to itself, which achieves nothing. In contrast, in the set_age2() procedure, "age" is a potential compilation error, so the compiler can assume you must have really meant "this.age := a" (and of course you’d still get an error if this.age was also invalid). Note that fields local to the class, which you might assume should "shadow" file-level or global identifiers, [at present] simply do not, and you still need the explicit "this." for those! Class methods offer the usual expected benefits over traditional static (file-level) routines, namely that they do not pollute the global namespace, and have full access to private fields. However, there is no need to jump through hoops just to share some code between classes: either invoke a more traditional routine from several places with sufficient parameters, or make enough fields public to allow the use of a traditional routine, or even use a traditional routine that invokes the occasional class method. In other words, Phix allows rather than forces code to be associated with a particular data type. TIP: for traditional routines, make the first parameter a class, ie explicitly do what the compiler does automatically between class and end class, and avoid trying to dot-call them, in other words use show(p) instead of p.show().