Classes and Constructors
In Klyn, a class is both a runtime type and the main unit of object-oriented design. This page
covers class declarations, constructors, instance and static members, file layout rules, and
the basics of working with this.
A Simple Class
public class Point:
public property x as Int = 0
public property y as Int = 0
public Point(x as Int = 0, y as Int = 0):
this.x = x
this.y = y
public override toString() as String:
return f"[{this.x}, {this.y}]"
Constructors are named after the class. There is no separate constructor or
new keyword.
Creating Objects
p1 = Point()
p2 = Point(10, 20)
p3 = Point(
1,
2
)
Constructor calls use ordinary call syntax and may span multiple lines when readability benefits.
Instance Members and this
Use this when you want to emphasize that a property or method belongs to the
current instance.
public class Counter:
public property current as Int = 0
public tick() as Void:
this.current++
public reset() as Void:
this.current = 0
Instance Storage
Ordinary class instances store one pointer to shared type metadata followed by their
instance fields. Primitive fields keep their physical widths: an Int uses
four bytes, a Short two, and a Byte one. Automatic properties
require backing storage; computed properties and static members do not.
class Point:
public property x as Int = 0
public property y as Int = 0
assert sizeof(Point) == 16 # 64-bit target: one pointer + two Int fields
sizeof reports the aligned inline instance layout, not the total memory
reachable through references, allocator bookkeeping, or shared reflection metadata.
It uses the operand's static type and does not evaluate the expression. This also works
for abstract classes, native classes, GUI widgets, enums, and concrete generic classes.
Native classes include their fixed native state; separately allocated buffers, collection
elements, and operating-system resources are not included.
import klyn.collections
import klyn.gui.windows
assert sizeof(Button("Not constructed")) == sizeof(Button)
assert sizeof(MainWindow) > sizeof(Object)
assert sizeof(Int[]) == sizeof(Array<Int>)
assert sizeof(f:[1, 2, 3]) == sizeof(Int[])
callback as (Int) -> Int = lambda(value): value + 1
assert sizeof(callback) == sizeof((Int) -> Int)
Interfaces and function signatures have no single concrete instance layout:
sizeof(List<Int>) and sizeof((Int) -> Int) report the
reference width, eight bytes on 64-bit platforms. This does not measure the implementing
object or a lambda's captured data. Void represents no data, so
sizeof(Void) is a compile-time error, as is an operand whose type is unknown.
See the String storage model for the
distinction between a string object's fixed size and its text payload.
Static Members
public class Settings:
public static defaultName = "Klyn"
print(Settings.defaultName)
Static members belong to the type, not to a specific instance.
Deep Copy and Receiver Typing
Every object inherits deepCopy() as this. The special return type
this means that the static result type follows the static receiver type. At runtime,
the copied object keeps its concrete subtype, and nested mutable objects and collections are
copied recursively. Cycles and shared references are preserved.
shape as Shape = Circle(10.0)
copy = shape.deepCopy() # statically Shape, dynamically Circle
assert copy is Shape
assert copy is Circle
assert copy is not shape
Because the static receiver above is Shape, the result cannot be assigned directly
to Circle without an explicit checked cast, even though its runtime type is a circle.
File and Public Class Rules
- A source file may declare at most one public top-level type.
- The public type must have the same name as its source file.
- If the file declares a package, the folder path must match that package.
package my.app.core
public class User:
pass
The example above belongs in my/app/core/User.kn.
Method Syntax Is Different from Function Syntax
Inside a class body, methods are declared directly with modifiers such as
public, private, or static. They do not use the
def keyword.