Skip to content

Encapsulation & Accessors

Encapsulation hides a class’s internal state behind a controlled surface. Zeus gives you access modifiers to mark what’s internal, and accessors (get/set) to expose properties that run code instead of touching a field directly.

Access modifiers

Every field and method carries one of three access levels:

  • public — reachable from anywhere.
  • private — reachable only from within the same class.
  • protected — reachable from within the same class and its subclasses, but not from outside the class hierarchy.
class Account {
private balance: i32;
public id: i32;
constructor(id: i32) {
this.id = id;
this.balance = 0;
}
public deposit(amount: i32): void {
this.balance = this.balance + amount;
}
public getBalance(): i32 {
return this.balance;
}
}
function main(): i32 {
let a: Account = new Account(1);
a.deposit(42);
// a.balance = 0; // error: 'balance' is private
return a.getBalance(); // => 42
}

Keeping balance private means it can only change through deposit, so the class controls its own invariants.

Protected: sharing with subclasses

private shuts a member off from everything but its own class — including subclasses. Use protected when a base class wants to keep a member internal to the outside world but still let its subclasses read and override it:

class Animal {
protected legs: i32;
constructor(legs: i32) { this.legs = legs; }
}
class Dog extends Animal {
constructor() { super(4); }
describe(): i32 {
return this.legs; // ok: protected members are visible to subclasses
}
}
function main(): i32 {
let d: Dog = new Dog();
// d.legs; // error: 'legs' is not accessible from outside the hierarchy
return d.describe() * 10 + 2; // => 42
}

If legs were private, the this.legs read inside Dog would be a compile error. See Inheritance for how subclasses build on their base.

Getters

A getter is a method declared with get that you read like a field. It’s ideal for values computed from internal state rather than stored directly:

class Circle {
private r: i32;
constructor(r: i32) { this.r = r; }
get area(): i32 {
return this.r * this.r; // computed on each read
}
}
function main(): i32 {
let c: Circle = new Circle(5);
return c.area; // read like a field, no parentheses => 25
}

Setters

Pair a getter with a set accessor to make an assignable property. The setter receives the assigned value as its parameter:

class Temp {
private c: i32;
constructor(c: i32) { this.c = c; }
get celsius(): i32 { return this.c; }
set celsius(v: i32) { this.c = v; }
}
function main(): i32 {
let t: Temp = new Temp(20);
let a: i32 = t.celsius; // getter => 20
t.celsius = 22; // setter
return a + t.celsius; // => 42
}

Read-only properties

A getter without a matching setter is read-only. Assigning to it is a compile error:

class Id {
private n: i32;
constructor(n: i32) { this.n = n; }
get id(): i32 { return this.n; } // no setter
}
function main(): i32 {
let x: Id = new Id(1);
// x.id = 5; // error: 'id' has no setter
return x.id; // => 1
}

Accessors through references

Accessors resolve on the receiver’s declared type, so they also work through parameters and nested fields — not just local variables:

class Box {
private v: i32;
constructor(v: i32) { this.v = v; }
get value(): i32 { return this.v; }
}
function readIt(b: Box): i32 {
return b.value; // getter dispatched via the parameter's type
}
function main(): i32 {
let b: Box = new Box(42);
return readIt(b); // => 42
}