Classes & Inheritance

Autolang supports object-oriented programming using classes and single inheritance.

Primary Constructor

A primary constructor is declared in the class header.

class className(parameter1: type, ...) { val member1: type = ... var member2: Int? func functionMember() { ... } }

All parameters in primary constructor will be members of class.

You can also declare additional properties and functions inside the class body.

🚀 Performance Tip: Native Bulk Initialization

We highly recommend using the Primary Constructor for better performance.

Since members are managed by internal IDs, the compiler recognizes that a Primary Constructor is dedicated solely to member initialization. Instead of executing multiple this.field = value assignments as separate bytecode instructions in the VM, AutoLang triggers a single native call to store the entire object data in one pass.

Example

class Person(val name: String, val age: Int, val id: Int) { // Optimized: All 3 fields are initialized natively at once func printInfo() { println("${name} [${id}] is ${age} years old") } } val john = Person("John", 25, 1001) john.printInfo()

A class with a primary constructor cannot define a secondary constructor.

Secondary Constructor

A class can define a secondary constructor using the constructor keyword. If a class extends another class, it must use a secondary constructor.

class User extends Object { // Slower: Executes sequential assignments constructor(position: Vector2, name: String) { super(position) println(name) } }
  • A secondary constructor is required when using extends
  • super(...) must be called
  • super(...) must be the first statement

Late Initialization

Use the lateinit modifier to declare a non-nullable property that will be initialized later. This allows you to bypass the compiler check for immediate initialization in the constructor.

class GameEngine { lateinit var config: String func init() { // Initialized later, not in constructor config = "Loaded Config" } func start() { println(config) } } val engine = GameEngine() engine.init() engine.start()

Access Modifiers

Autolang supports access control using the following modifiers:

  • public - accessible everywhere (default)
  • private - accessible only inside the same class
  • protected - accessible inside the class and its subclasses
class Account { private var id: Int? protected var balance: Int? constructor(id: Int, balance: Int) { this.id = id this.balance = balance } public func deposit(amount: Int) { balance = balance! + amount } private func logInternal() { println("Internal log") } } class SavingAccount extends Account { constructor() { super(1, 1000) println(balance) // allowed (protected) } } SavingAccount()

Method Override

Use the @override modifier to override a base method.

class Animal(val name: String) { func sound() { } } class Cat extends Animal { constructor() { super("cat") } @override func sound() { println("Meow") } } class Dog extends Animal { constructor() { super("dog") } @override func sound() { println("Hello guys!!!") } } val dog: Animal = Dog() val cat: Animal = Cat() dog.sound() cat.sound()

Preventing Override

Use the @no_override annotation to prevent subclasses from overriding a specific method. This effectively makes the method "final".

class Base { @no_override func coreLogic() { println("Core logic cannot be changed") } } class Child extends Base { constructor() { super() } // Uncommenting the code below will cause a compilation error: /* @override func coreLogic() { println("Trying to change logic") } */ } val c = Child() c.coreLogic()

Static Members

Static members belong to the class, not instances.

class Person() class X extends Person { static val k: Int = 99 constructor() { super() } } println(X.k)

Index Operator Overloading

You can define how your objects behave with the index operator [] by implementingget and set functions. This works for any class, allowing you to create custom collections or data wrappers.

class DailyTemperatures { // Internal storage private val temps: Array<Int> = Array<Int>() // Adds support for: val t = obj[i] func get(index: Int): Int { return temps.get(index) } // Adds support for: obj[i] = value func set(index: Int, value: Int) { temps.set(index, value) } func add(t: Int) { temps.add(t) } } val data = DailyTemperatures() data.add(30) data.add(32) // Syntactic sugar: Calls data.set(1, 35) data[1] = 35 // Syntactic sugar: Calls data.get(1) println("Temperature: ${data[1]}")

Type Checking

Use the is operator for runtime type checking.

class Animal() class Cat extends Animal { constructor(){ super() } } class Dog extends Animal { constructor(){ super() } } val x = Cat() println(x is Cat) println(x is Animal) println(x is Float)