Classes & OOP
Quartz features a comprehensive object-oriented programming system inspired by Java and C++, with clean, modern syntax that prioritizes developer experience.
Class Declaration
Define a class using the class keyword:
class Person {
// Fields (instance variables)
string name;
int age;
bool active;
// Methods (instance functions)
fn greet() {
io.out.println("Hello, I'm", name);
}
fn haveBirthday() {
age += 1;
io.out.println(name, "is now", age);
}
}
Object Creation
Create instances using the new keyword:
let person = new Person();
// Set field values
person.name = "Alice";
person.age = 30;
person.active = true;
// Call methods
person.greet(); // "Hello, I'm Alice"
person.haveBirthday(); // "Alice is now 31"
Fields
Fields are instance variables that belong to each object:
class Rectangle {
double width;
double height;
string color;
}
let rect = new Rectangle();
rect.width = 10.0;
rect.height = 5.0;
rect.color = "blue";
io.out.println("Width:", rect.width); // 10.0
Supported Field Types
int- 32-bit integersdouble- 64-bit floating pointstring- Text stringsbool- Boolean values- Custom class names - For composition
Methods
Methods are functions that belong to a class:
class Calculator {
int value;
// Method with no return value
fn reset() {
value = 0;
}
// Method with parameters
fn add(n: int) {
value += n;
}
// Method with return value
fn getValue() -> int {
return value;
}
// Method with multiple parameters
fn multiply(a: int, b: int) -> int {
return a * b;
}
}
let calc = new Calculator();
calc.reset();
calc.add(10);
calc.add(5);
io.out.println("Value:", calc.getValue()); // 15
io.out.println("3 * 4 =", calc.multiply(3, 4)); // 12
Return Statements
Use return to exit a method and optionally return a value:
class Math {
fn abs(x: int) -> int {
if (x < 0) {
return -x; // Early return
}
return x;
}
fn isEven(n: int) -> bool {
return n % 2 == 0;
}
fn doSomething() {
// Void methods can omit return
io.out.println("Done");
}
}
Inheritance
Create class hierarchies using extends:
class Animal {
string name;
int age;
fn speak() {
io.out.println("Animal sound");
}
fn describe() {
io.out.println(name, "is", age, "years old");
}
}
class Dog extends Animal {
string breed;
fn speak() {
io.out.println("Woof! Woof!");
}
fn fetch() {
io.out.println(name, "fetches the ball");
}
}
class Cat extends Animal {
bool indoor;
fn speak() {
io.out.println("Meow!");
}
fn purr() {
io.out.println(name, "purrs contentedly");
}
}
// Usage
let dog = new Dog();
dog.name = "Buddy";
dog.age = 3;
dog.breed = "Golden Retriever";
dog.describe(); // "Buddy is 3 years old"
dog.speak(); // "Woof! Woof!"
dog.fetch(); // "Buddy fetches the ball"
Access Modifiers
Quartz supports visibility modifiers for encapsulation:
class BankAccount {
public string owner;
private double balance;
protected string accountId;
public fn deposit(amount: double) {
if (amount > 0) {
balance += amount;
}
}
public fn getBalance() -> double {
return balance;
}
private fn generateId() -> string {
return "ACC-12345";
}
}
| Modifier | Visibility |
|---|---|
| `public` | Accessible from anywhere |
| `private` | Accessible only within the class |
| `protected` | Accessible within class and subclasses |
Static Methods
Static methods belong to the class, not instances:
class MathUtils {
static fn max(a: int, b: int) -> int {
if (a > b) {
return a;
}
return b;
}
static fn min(a: int, b: int) -> int {
if (a < b) {
return a;
}
return b;
}
}
// Call without creating an instance
let maximum = MathUtils.max(10, 20); // 20
Composition
Classes can contain instances of other classes:
class Point {
double x;
double y;
fn distanceTo(other: Point) -> double {
let dx = other.x - x;
let dy = other.y - y;
return math.sqrt(dx * dx + dy * dy);
}
}
class Circle {
Point center;
double radius;
fn area() -> double {
return 3.14159 * radius * radius;
}
fn contains(point: Point) -> bool {
return center.distanceTo(point) <= radius;
}
}
let circle = new Circle();
circle.center = new Point();
circle.center.x = 0.0;
circle.center.y = 0.0;
circle.radius = 5.0;
io.out.println("Area:", circle.area());
Complete Example
import system.io as io;
import system.math as math;
class Shape {
string name;
string color;
fn describe() {
io.out.println("A", color, name);
}
}
class Circle extends Shape {
double radius;
fn area() -> double {
return 3.14159 * radius * radius;
}
fn circumference() -> double {
return 2.0 * 3.14159 * radius;
}
}
class Rectangle extends Shape {
double width;
double height;
fn area() -> double {
return width * height;
}
fn perimeter() -> double {
return 2.0 * (width + height);
}
fn isSquare() -> bool {
return width == height;
}
}
// Create shapes
let circle = new Circle();
circle.name = "Circle";
circle.color = "red";
circle.radius = 5.0;
let rect = new Rectangle();
rect.name = "Rectangle";
rect.color = "blue";
rect.width = 10.0;
rect.height = 5.0;
// Use them
circle.describe();
io.out.println("Area:", circle.area());
io.out.println("Circumference:", circle.circumference());
io.out.println("");
rect.describe();
io.out.println("Area:", rect.area());
io.out.println("Perimeter:", rect.perimeter());
io.out.println("Is square:", rect.isSquare());
Best Practices
- Use meaningful names: Class names should be nouns (Person, Rectangle)
- Single responsibility: Each class should have one purpose
- Encapsulation: Keep fields private when possible
- Composition over inheritance: Prefer containing objects over deep hierarchies
- Keep methods small: Methods should do one thing well