Why does EK9 require explicit constructors for uninitialised properties?
← Classes and OOP · Ref: Q104
EK9 requires that every property is either given a default value at declaration or initialised in a constructor. If a property is declared without a value, the compiler requires a constructor that sets it. This prevents null surprises.
THE RULE
Properties declared without values must be initialised in a constructor:
Connection
host as String?
port as Integer?
Connection()
-> host as String, port as Integer
this.host: host
this.port: port
The '?' suffix means 'declared but not yet initialised'. Without '?' the compiler rejects the declaration immediately with E08180.
FLOW ANALYSIS ENFORCEMENT
EK9 uses flow analysis to verify that every property is initialised before use:
E08180 — property not marked '?' and not initialised (must use '?' or provide a value) E08070 — property declared with '?' but never assigned in any constructor E08060 — property used before it has been initialised (would be NullPointerException in Java)
This eliminates the entire category of 'field not initialised' bugs at compile time.
WHY THIS MATTERS
In Java, fields default to null. You discover the problem at runtime with NullPointerException. In Python, missing attributes cause AttributeError at runtime. In Kotlin, lateinit crashes at runtime if accessed before init. EK9 catches ALL of these at compile time.
HOW TO SATISFY THE RULE
Option 1: Provide a default value at declaration:
host <- "localhost"
Option 2: Use unset default (type constructor):
host <- String()
Option 3: Declare without value and initialise in constructor:
host as String? MyClass() -> host as String; this.host: host
Note: property initialisers must be simple — literals, type constructors, or literal collections only. Function calls and expressions are not allowed (E04050). Use a constructor for computed values.
See Q29 for unset variables. See Q94 for constructors. See Q93 for class basics. See Q632 for define-before-use patterns. See Q633 for initialisation across branches. See Q692 for field initialisation patterns.
Example
defines module qa.oop.uninitialised defines class Connection host as String? port as Integer? default private Connection() Connection() -> host as String port as Integer this.host: host this.port: port describe() <- rtn as String: `${host}:${port}` default operator ? DatabaseConfig host <- "localhost" port <- 5432 database <- String() DatabaseConfig() -> database as String this.database: database describe() <- rtn as String: `${host}:${port}/${database}` default operator ? defines program UninitialisedDemo() stdout <- Stdout() // === UNINITIALISED PROPERTIES: must be set in constructor === conn <- Connection("db.example.com", 3306) stdout.println(`Connection: ${conn.describe()}`) stdout.println(`IsSet: ${conn?}`) // === MIX: some defaults, some uninitialised === db <- DatabaseConfig("myapp") stdout.println(`Database: ${db.describe()}`) // === Default values work without constructor args === stdout.println(`DB isSet: ${db?}`)
Common mistakes
E08180 — Properties declared without '?' and without a default value must be initialised inline. The '?' suffix marks a property as explicitly uninitialised, requiring constructor assignment. If neither '?' nor a default is provided the compiler may report E08180. See ek9 -h E08180 for details.
Incorrect:
host as String
Correct:
host as String?
E08060 — If a constructor does not initialise all uninitialised properties, the compiler's flow analysis detects that 'port' is never assigned any value and reports E08060 — variable declared but never initialised. Every property must be set on every code path. See ek9 -h E08060 for details.
Incorrect:
this.host: host
Correct:
this.host: host this.port: port
E08060 — If the constructor forgets to initialise 'host' but a method later reads it, E08060 is triggered — is/may not be initialised before use. In Java this would be a NullPointerException at runtime. EK9 catches it at compile time through flow analysis. See ek9 -h E08060 for details.
Incorrect:
this.port: port
Correct:
this.host: host this.port: port
E50001 — Property initialisers must be simple values: a literal ('localhost'), a type constructor (String()), or a literal collection. Function calls and expressions are not allowed in property declarations. Java developers are used to 'private String host = computeDefault()' but EK9 requires E50001 — type must be a simple aggregate/list/dict. Use a constructor to compute values instead. See ek9 -h E50001 for details.
Incorrect:
host <- defaultHost()
Correct:
host <- "localhost"
E08180 — Properties declared without '?' and without a default value must be initialised inline. The '?' suffix marks a property as explicitly uninitialised, requiring constructor assignment. If neither '?' nor a default is provided, E08180 is triggered. See ek9 -h E08180 for details.
Incorrect:
host as String
Correct:
host as String?
E04050 — Property initialisers must be simple values: a literal ('localhost'), a type constructor (String()), or a literal collection. Function calls and expressions like String().trim() are not allowed in property declarations. Use a constructor for computed values. See ek9 -h E04050 for details.
Incorrect:
host <- String().trim()
Correct:
host <- "localhost"
Other ways to ask this
- What happens if a class property has no default value in EK9?
- How do I handle uninitialised fields in an EK9 class?
- Why must I write a constructor when a property is not initialised?
Coming from another language?
Java: fields default to null/0/false, no compile-time enforcement of initialisation. Python: no field declarations, set in __init__, AttributeError if accessed before set. Rust: all fields must be initialised in struct literal, compiler enforces. Go: fields zero-valued by default (empty string, 0, nil). Kotlin: lateinit var for deferred initialisation, crashes at runtime if accessed too early. EK9: compile-time enforcement that properties are either declared with values or initialised in constructors, no null defaults.
Keywords: analysis, field, compile, NullPointerException, null, migrate, value, constructor, object-oriented, safety, default, initialise, uninitialized, flow, uninitialised, property, lateinit, required