Classes
A class is used to define a reusable type that contains properties (data) and functions (behavior). You see a class as a blueprint that can be used to create multiple objects from the class.
class Character {
Name = "Unknown"
Health = 100
Print() {
print("Name: " + this.Name)
print("Health: " + this.Health)
}
}
local hero = new Character()
hero.Name = "Lithrun"
hero.Health = 150;
hero.Print()
// Output:
// Name: Lithrun
// Health: 150
In above example:
Characteris the name of the classNameandHealthare fieldsPrintis a function that can be invoked on any Character objectnew Character()creates an instance of the class
Why classes are useful:
- It groups data and logic together
- It makes the code easier to organize
- It allows you to create many instances from the data predefined structure
Fields
A field is a variable that is stored in the instance of a class.
// A character has a Name field and a Health field
class Character {
Name = "Unknown";
Health = 100;
}
local hero = neww Character()
print(hero.Name) // Unknown
hero.Name = "Lithrun" // We now override the field with a new value
print(hero.Name) // Lithrun
Methods
A class can also define functions, which we refer to as methods. A method defines behavior of a class and can read or change its fields.
class Character {
Name = "Unknown";
Health = 100;
ChangeHealth(health) {
this.Health += health
}
IsAlive() {
return this.Health >= 0
}
PrintStatus() {
if (this.IsAlive()) {
print(this.Name + " is alive with " + this.Health + "HP")
} else {
print(this.Name + " has died")
}
}
}
local hero = new Character()
hero.Name = "Lithrun"
hero.PrintStatus() // Lithrun is alive with 100HP
hero.ChangeHealth(-100)
hero.PrintStatus() // Lithrun has died
Constructor
A constructor is a special function that runs when a new instance of a class is created. This is used to set up the object with some default values.
class Character {
Name = "Unknown";
Health = 100;
// Constructor
Character(name, health) {
this.Name = name
this.Health = health
}
}
local hero = new Character("Lithrun", 150) // { "Name": "Unknown", "Health": 150 }
It’s also possible to define multiple constructors
class Character {
Name = "Unknown";
Health = 0;
Character(name) {
this.Name = name;
this.Health = 100;
}
Character(name, health) {
this.Name = name;
this.Health = health;
}
}
local heroA = new Character("Lithrun") // Health is 100 as the 1st constructor was used
local heroB = new Character("Hergan", 200) // Health is 200, since we used the 2nd constructor
This
As seen in the constructor example, there is a this keyword. This refers to the current object inside a class. It lets a function or constructor access that objects fields and other methods.
class Character {
Name = "Unknown";
Health = 0;
Character(name, health) {
this.Name = name; // Using this to refer to the field of the Character
this.Health = health;
}
TakeDamage(Health) {
this.Health -= Health // Using this also helps to avoid confusion between fields and function parameters
}
}
local hero = new Character("Lithrun", 100)
hero.TakeDamage(25) // Health is now 75
Static
Static is useful for:
- Shared values
- Helper functions related to the class
- Data that should not exist on every instance
Static Field
A static field stores a value that is shared by the whole class. To access a static field, use the class name followed by the field name.
class Math {
static PI = 3.1415926535
}
print(Math.PI)
local math = new Math()
print(math.PI) // Error, because PI belongs to the class, not the instance
Static Method
A static method is a function that belongs to the class itself. To call a static method, use the class name followed by the method name.
class Math {
static function Add(a, b) {
return a + b
}
}
print(Math.Add(5, 10)) // 15
Static Class
When a class is marked as static, it cannot be instantiated. A static class cannot have a constructor, because no instances of it can be created. A static class is useful for utlity classes that only contain shared values or helpers functions.
static class MathHelpers {
static PI = 3.14
}
print(MathHelpers.PI) // 3.14
local math = new MathHelpers() // Error, as a static class cannot have an instance
Inheritance
Classes also support inheritence. This lets one class build on another class. A derived class inherits the fields and functions of a base class, and can then add its own extra behavior.
class Item {
Name = "Unknown Item"
ShowName() {
print("Item: " + this.Name)
}
}
class Weapon : Item {
Damage = 0;
ShowStats() {
print(this.Name + " deals " + this.Damage + " damage")
}
}
local sword = new Weapon()
sword.Name = "Iron Sword" // The Weapon Class inherited the Name field
sword.Damage = 12
sword.ShowName() // Item: Iron Sword
sword.ShowStats() // Iron Sword deals 12 damage
Why use inheritence:
- It reduces repeated code
- It lets related classes share common behavior
- It helpers organize types into clear relationships
Base
The base keyword refers to the parent class from inside a child class. It is mainly used for two things:
- Calling the parent constructor
- Calling a parent method
A constructor or method can be overriden from the parent class if it has the same name and parameters.
Base Constructor
Use base(...) inside the child constructor to initialize the parent of the object
class Item {
Item(name) {
this.Name = name
}
}
class Weapon : Item {
Weapon(name, damage) {
base(name) // Calls the Item(name) constructor so we don't need to duplicate the logic
this.Damage = damage
}
}
var sword = new Weapon("Iron Sword", 12)
print(sword.Name) // Iron Sword
print(sword.Damage) // 12
Base Method
class Item {
ShowName() {
print("Item: " + this.Name)
}
}
class Weapon : Item {
ShowStats() {
base.ShowName() // Whenever ShowsStats is called, the ShowName of Item is also called
print("Damage: " + this.Damage)
}
}
var sword = new Weapon()
sword.Name = "Iron Sword"
sword.Damage = 12
sword.ShowStats() // Item: IronSword, Damage: 12
Overriding a method
To override a method from the parent class, use the same method name and parameter types.
class Item {
Describe() {
print("This is an item")
}
}
class Weapon : Item {
Describe() {
print("This is a weapon")
}
}
var sword = new Weapon()
sword.Describe() // This is a weapon
When overriding the method, it’s still possible to call the parents original method with the base keyword.
class Item {
Describe() {
print("Item: " + this.Name)
}
}
class Weapon : Item {
Describe() {
base.Describe()
print("Damage: " + this.Damage)
}
}
var sword = new Weapon()
sword.Name = "Iron Sword"
sword.Damage = 12
sword.Describe() // Item: Iron Sword, Damage: 12
Sealed
The sealed keyword is related to inheritence. When a class is marked as sealed, then it cannot be inherited. If a method or field is marked as field, then it cannot be overwrittene.
Sealed Class
sealed class Item {
}
// Throws an error, since Item is sealed, so Weapon cannot derive from Item
class Weapon : Item {
}
Sealed Method
class Item {
Name = "Item"
sealed Describe() {
print("This is an item named: " + this.Name)
}
}
class Weapon : Item {
// Error, as Describe cannot be overridden
Describe() {
print("This is a weapon named: + this.Name)
}
}
Sealed Field
class Item {
sealed Name = "Item"
Describe() {
print("This is an item named: " + this.Name)
}
}
class Weapon : Item {
// Error, as Name cannot be overridden
Name = "Weapon"
Describe() {
print("This is a weapon named: + this.Name)
}
}
Access Modifiers
Access modifiers decide who can see and use a field or method. There are 3 access modifiers:
publicaccessible from anywhere. If no access modifier is specified, then it’s considered publicprivateonly accessible inside the same classprotectedonly accessible inside the class and derived classes
Public
public means a field or method can be accessed from anywhere. It can be read, called, or modified by other code. Use public for members that are meant to be used outside the class, such as fields / methods that other objects should interact with.
class Weapon {
public Damage = 0;
// If no access modifier is specified, then it is public
Name = "Test"
Weapon(damage) {
this.Damage = damage
}
public Print() {
return FormatString(this.Damage + " Damage")
}
}
local weapon = new Weapon(10);
print(weapon.Damage) // 10
weapon.Print() // 10 Damage
weapon.Damage = 25 // 25
print(weapon.Name) // Test
weapon.Name = "Lithrun"
Private
private means a field or function can only be accessed inside the class that declares it. It is completed hidden from outside the class and also from derived classes. This is useful for internal implementation details or data that should only get changed through controlled class logic. A common pattern is to keep a field private, and expose a public method that reads or modifies it.
class Weapon {
private Damage = 0;
Weapon(damage) {
this.Damage = damage
}
// A common pattern to have immutable fields is to mark them as private
// Then the value can be retrieved but never changed
GetDamage() {
return this.Damage
}
public Print() {
return FormatString(this.Damage + " Damage")
}
private FormatString(s) {
return "[Weapon] " + s;
}
}
local weapon = new Weapon(10);
print(weapon.GetDamage()) // 10
weapon.Print() // [Weapon] 10 Damage
print(weapon.Damage) // Error, as Damage is private thus it cannot be accessed
weapon.Damage = 25 // Error, as Damage is private thus it cannot be modified
weapon.FormatString("test") // Error, as FormatString is a private method
Protected
protected allows a field or function to be used inside the declaring class and any class that inherits from it, while still hiding it from public access. It is useful when child classes need access to a shared internal state, but that state should not be part of the public API
class Item {
protected Name = "";
Item(name) {
this.Name = name
}
protected FormatName() {
return "Item: " + this.Name
}
}
class Weapon : Item {
Weapon(name) {
base(name)
}
PrintName() {
// Protected fields and methods can be accessed inside derived classes
print(this.FormatName())
}
}
local weapon = new Weapon("Iron Sword");
weapon.PrintName() // Item: Iron Sword
print(weapon.Name) // Error, as Name is protected and cannot be accessed from outside the class hierarchy
print(weapon.FormatName()) // Error, as FormatName is protected and cannot be called from outside the class hierarchy
weapon.Name = "Steel Sword" // Error, as Name is protected and cannot be modified from outside the class hierarchy