Why is List of Circle not compatible with List of Shape?
← Generics · Ref: Q653
EK9 creates truly independent types for each generic parameterisation. 'List of Circle' and 'List of Shape' are completely separate types with no relationship, even if Circle extends Shape.
TYPE INDEPENDENCE
Each parameterisation is a distinct type:
List of Circle // one type List of Shape // completely different type List of Integer // yet another different type
NO ERASURE
Unlike Java where 'List<Circle>' and 'List<Shape>' become the same 'List' at runtime (erasure), EK9 maintains full type identity. They are separate types that happen to have similar structures.
NO COVARIANCE
You cannot pass 'List of Circle' where 'List of Shape' is expected. This prevents the type safety violation where adding a Square to a 'List of Shape' that is actually a 'List of Circle'.
METHOD OVERLOADING
Because types are independent, you can overload methods:
process(circles as List of Circle) process(shapes as List of Shape)
Java cannot do this due to erasure.
See Q198 for built-in generics. See Q195 for constrain by. See Q196 for multi-parameter generics. See Q655 for method overloading with generics. See Q642 for constructor inference.
Example
defines module qa.genericsdeep.typeindependence defines class Colour as abstract colourName as String: String() default Colour() as pure Colour() as pure -> n as String colourName :=? String(n) name() as pure <- rtn as String: String(colourName) default operator ? Red is Colour default Red() as pure Red() as pure -> n as String super(n) Blue is Colour default Blue() as pure Blue() as pure -> n as String super(n) defines program TypeIndependenceDemo() stdout <- Stdout() // === SEPARATE LISTS OF DIFFERENT TYPES === reds <- List() of Red reds += Red("Crimson") reds += Red("Scarlet") blues <- List() of Blue blues += Blue("Navy") // === EACH LIST IS AN INDEPENDENT TYPE === redCount <- length reds stdout.println(`Red count: ${redCount}`) blueCount <- length blues stdout.println(`Blue count: ${blueCount}`) // === CANNOT MIX: List of Red != List of Blue === // reds += Blue("Teal") // ERROR: Blue is not Red // === INDEPENDENT CONTAINERS === redOpt <- Optional(Red("Vermillion")) blueOpt <- Optional(Blue("Azure")) stdout.println(`Red optional set: ${redOpt?}`) stdout.println(`Blue optional set: ${blueOpt?}`) // Optional of Red and Optional of Blue are different types stdout.println("Each parameterised type is a separate independent type")
Common mistakes
E50060 — Each generic parameterisation creates a fully independent type. List of Red and List of Blue are completely separate types with no relationship, so adding a Blue to a List of Red is a type error. See ek9 -h E50060 for details.
Incorrect:
reds <- List() of Red reds += Blue("Navy")
Correct:
reds <- List() of Red
Other ways to ask this
- How does type independence work for parameterised types in EK9?
- Does EK9 use type erasure like Java?
- Can I pass List of Circle where List of Shape is expected?
Coming from another language?
Java: type erasure makes List<Circle> and List<Shape> the same at runtime, cannot overload. C++: templates create independent types (full monomorphisation). Rust: generics create independent types. Go: generics create independent types (Go 1.18+). Kotlin: same erasure as Java. EK9: fully independent types, no erasure, safe method overloading.
Keywords: covariance, erasure, overload, migrate, independence, list, generic, type, separate, type-parameter, parameterised