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.

A member declared without a modifier is private by default (encapsulation-first, unlike TypeScript). Write public explicitly on any field or method you want to access from outside the class — e.g. public name: string;.

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;
}
}
let a: Account = new Account(1);
a.deposit(42);
// a.balance = 0; // error: 'balance' is private
console.log(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
}
}
let d: Dog = new Dog();
// d.legs; // error: 'legs' is not accessible from outside the hierarchy
console.log(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
}
}
let c: Circle = new Circle(5);
console.log(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; }
}
let t: Temp = new Temp(20);
let a: i32 = t.celsius; // getter => 20
t.celsius = 22; // setter
console.log(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
}
let x: Id = new Id(1);
// x.id = 5; // error: 'id' has no setter
console.log(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
}
let b: Box = new Box(42);
console.log(readIt(b)); // 42