[{"data":1,"prerenderedAt":3034},["ShallowReactive",2],{"page-\u002Fscala\u002F06-classes-and-objects":3},{"id":4,"title":5,"body":6,"description":3025,"extension":3026,"meta":3027,"navigation":171,"path":3028,"seo":3029,"stem":3032,"__hash__":3033},"content\u002Fscala\u002F06-classes-and-objects.md","Scala — Classes, Traits & the Object Model",{"type":7,"value":8,"toc":3011},"minimark",[9,14,19,424,428,893,897,1147,1154,1545,1552,1812,1816,2232,2236,2579,2583,2597,2613,2702,2719,2723,2745,2868,2888,2899,2903,2918,3007],[10,11,13],"h1",{"id":12},"_06-classes-traits-the-object-model","06 — Classes, Traits & the Object Model",[15,16,18],"h2",{"id":17},"class-construction-constructor-fields","Class Construction — Constructor & Fields",[20,21,23],"code-wrapper",{"language":22},"scala",[24,25,29],"pre",{"className":26,"code":27,"language":22,"meta":28,"style":28},"language-scala shiki shiki-themes github-light github-dark","\u002F\u002F Scala 3: primary constructor parameters are in the class signature.\n\u002F\u002F `val` params become fields with getters. Bare params are local to constructor body.\nfinal class Server(val host: String, val port: Int, config: Config):\n  require(port > 0 && port \u003C= 65535, s\"invalid port: $port\")  \u002F\u002F precondition → IllegalArgumentException\n  require(host.nonEmpty, \"host must not be empty\")\n\n  \u002F\u002F Constructor body — runs during instantiation, before any method can be called\n  private val startTime: Long = System.nanoTime()  \u002F\u002F private field, no setter\n  private var requestCount: Long = 0L              \u002F\u002F private mutable, no external setter\n\n  \u002F\u002F Public method returning a field (JIT inlines this to a direct field read)\n  def uptimeMs: Long = (System.nanoTime() - startTime) \u002F 1_000_000\n\n  \u002F\u002F Synchronized increment — but NOT atomic! See concurrency chapter.\n  def recordRequest(): Unit = synchronized {\n    requestCount += 1\n  }\n\n  \u002F\u002F Override toString — auto-generated for case classes, manual for regular classes\n  override def toString: String = s\"Server($host:$port, requests=$requestCount)\"\n\n\u002F\u002F Instantiation — no 'new' needed in Scala 3 for classes with companion apply\nval server = Server(\"api.example.com\", 443, defaultConfig)\n\u002F\u002F JVM: Server.\u003Cinit>(String, int, Config) — parameters are fields, not a separate init block\n","",[30,31,32,41,47,108,154,166,173,179,208,231,236,242,278,283,289,313,325,331,336,342,382,387,393,418],"code",{"__ignoreMap":28},[33,34,37],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"sdCPZ","\u002F\u002F Scala 3: primary constructor parameters are in the class signature.\n",[33,42,44],{"class":35,"line":43},2,[33,45,46],{"class":39},"\u002F\u002F `val` params become fields with getters. Bare params are local to constructor body.\n",[33,48,50,54,57,61,65,68,72,75,78,81,83,86,88,91,93,96,99,102,105],{"class":35,"line":49},3,[33,51,53],{"class":52},"svdQ7","final",[33,55,56],{"class":52}," class",[33,58,60],{"class":59},"sIsaT"," Server",[33,62,64],{"class":63},"ssxIu","(",[33,66,67],{"class":52},"val",[33,69,71],{"class":70},"sCrzJ"," host",[33,73,74],{"class":52},":",[33,76,77],{"class":59}," String",[33,79,80],{"class":63},", ",[33,82,67],{"class":52},[33,84,85],{"class":70}," port",[33,87,74],{"class":52},[33,89,90],{"class":59}," Int",[33,92,80],{"class":63},[33,94,95],{"class":70},"config",[33,97,98],{"class":63},": ",[33,100,101],{"class":59},"Config",[33,103,104],{"class":63},")",[33,106,107],{"class":52},":\n",[33,109,111,114,117,121,124,127,130,133,135,138,142,145,148,151],{"class":35,"line":110},4,[33,112,113],{"class":63},"  require(port ",[33,115,116],{"class":52},">",[33,118,120],{"class":119},"snvgF"," 0",[33,122,123],{"class":52}," &&",[33,125,126],{"class":63}," port ",[33,128,129],{"class":52},"\u003C=",[33,131,132],{"class":119}," 65535",[33,134,80],{"class":63},[33,136,137],{"class":52},"s",[33,139,141],{"class":140},"sJ6F3","\"invalid port: ",[33,143,144],{"class":63},"$port",[33,146,147],{"class":140},"\"",[33,149,150],{"class":63},")  ",[33,152,153],{"class":39},"\u002F\u002F precondition → IllegalArgumentException\n",[33,155,157,160,163],{"class":35,"line":156},5,[33,158,159],{"class":63},"  require(host.nonEmpty, ",[33,161,162],{"class":140},"\"host must not be empty\"",[33,164,165],{"class":63},")\n",[33,167,169],{"class":35,"line":168},6,[33,170,172],{"emptyLinePlaceholder":171},true,"\n",[33,174,176],{"class":35,"line":175},7,[33,177,178],{"class":39},"  \u002F\u002F Constructor body — runs during instantiation, before any method can be called\n",[33,180,182,185,188,191,193,196,199,202,205],{"class":35,"line":181},8,[33,183,184],{"class":52},"  private",[33,186,187],{"class":52}," val",[33,189,190],{"class":70}," startTime",[33,192,74],{"class":52},[33,194,195],{"class":59}," Long",[33,197,198],{"class":52}," =",[33,200,201],{"class":59}," System",[33,203,204],{"class":63},".nanoTime()  ",[33,206,207],{"class":39},"\u002F\u002F private field, no setter\n",[33,209,211,213,216,219,221,223,225,228],{"class":35,"line":210},9,[33,212,184],{"class":52},[33,214,215],{"class":52}," var",[33,217,218],{"class":70}," requestCount",[33,220,74],{"class":52},[33,222,195],{"class":59},[33,224,198],{"class":52},[33,226,227],{"class":119}," 0L",[33,229,230],{"class":39},"              \u002F\u002F private mutable, no external setter\n",[33,232,234],{"class":35,"line":233},10,[33,235,172],{"emptyLinePlaceholder":171},[33,237,239],{"class":35,"line":238},11,[33,240,241],{"class":39},"  \u002F\u002F Public method returning a field (JIT inlines this to a direct field read)\n",[33,243,245,248,251,253,255,257,260,263,266,269,272,275],{"class":35,"line":244},12,[33,246,247],{"class":52},"  def",[33,249,250],{"class":59}," uptimeMs",[33,252,74],{"class":52},[33,254,195],{"class":59},[33,256,198],{"class":52},[33,258,259],{"class":63}," (",[33,261,262],{"class":59},"System",[33,264,265],{"class":63},".nanoTime() ",[33,267,268],{"class":52},"-",[33,270,271],{"class":63}," startTime) ",[33,273,274],{"class":52},"\u002F",[33,276,277],{"class":119}," 1_000_000\n",[33,279,281],{"class":35,"line":280},13,[33,282,172],{"emptyLinePlaceholder":171},[33,284,286],{"class":35,"line":285},14,[33,287,288],{"class":39},"  \u002F\u002F Synchronized increment — but NOT atomic! See concurrency chapter.\n",[33,290,292,294,297,300,302,305,307,310],{"class":35,"line":291},15,[33,293,247],{"class":52},[33,295,296],{"class":59}," recordRequest",[33,298,299],{"class":63},"()",[33,301,74],{"class":52},[33,303,304],{"class":59}," Unit",[33,306,198],{"class":52},[33,308,309],{"class":52}," synchronized",[33,311,312],{"class":63}," {\n",[33,314,316,319,322],{"class":35,"line":315},16,[33,317,318],{"class":63},"    requestCount ",[33,320,321],{"class":52},"+=",[33,323,324],{"class":119}," 1\n",[33,326,328],{"class":35,"line":327},17,[33,329,330],{"class":63},"  }\n",[33,332,334],{"class":35,"line":333},18,[33,335,172],{"emptyLinePlaceholder":171},[33,337,339],{"class":35,"line":338},19,[33,340,341],{"class":39},"  \u002F\u002F Override toString — auto-generated for case classes, manual for regular classes\n",[33,343,345,348,351,354,356,358,360,363,366,369,371,373,376,379],{"class":35,"line":344},20,[33,346,347],{"class":52},"  override",[33,349,350],{"class":52}," def",[33,352,353],{"class":59}," toString",[33,355,74],{"class":52},[33,357,77],{"class":59},[33,359,198],{"class":52},[33,361,362],{"class":52}," s",[33,364,365],{"class":140},"\"Server(",[33,367,368],{"class":63},"$host",[33,370,74],{"class":140},[33,372,144],{"class":63},[33,374,375],{"class":140},", requests=",[33,377,378],{"class":63},"$requestCount",[33,380,381],{"class":140},")\"\n",[33,383,385],{"class":35,"line":384},21,[33,386,172],{"emptyLinePlaceholder":171},[33,388,390],{"class":35,"line":389},22,[33,391,392],{"class":39},"\u002F\u002F Instantiation — no 'new' needed in Scala 3 for classes with companion apply\n",[33,394,396,398,401,403,405,407,410,412,415],{"class":35,"line":395},23,[33,397,67],{"class":52},[33,399,400],{"class":70}," server",[33,402,198],{"class":52},[33,404,60],{"class":59},[33,406,64],{"class":63},[33,408,409],{"class":140},"\"api.example.com\"",[33,411,80],{"class":63},[33,413,414],{"class":119},"443",[33,416,417],{"class":63},", defaultConfig)\n",[33,419,421],{"class":35,"line":420},24,[33,422,423],{"class":39},"\u002F\u002F JVM: Server.\u003Cinit>(String, int, Config) — parameters are fields, not a separate init block\n",[15,425,427],{"id":426},"traits-linearization-the-diamond-resolution","Traits & Linearization — The Diamond Resolution",[20,429,430],{"language":22},[24,431,433],{"className":26,"code":432,"language":22,"meta":28,"style":28},"\u002F\u002F Traits can have concrete methods, fields, and initialization logic.\n\u002F\u002F When multiple traits are mixed in, Scala uses LINEARIZATION to resolve\n\u002F\u002F method dispatch — rightmost trait wins, `super` calls chain up.\n\ntrait Logger:\n  def log(msg: String): Unit = println(s\"[LOG] $msg\")\n\ntrait TimestampedLogger extends Logger:\n  override def log(msg: String): Unit =\n    super.log(s\"${System.currentTimeMillis()} | $msg\")  \u002F\u002F super → next in linearization\n\ntrait JsonLogger extends Logger:\n  override def log(msg: String): Unit =\n    super.log(s\"\"\"{\"msg\":\"$msg\"}\"\"\")          \u002F\u002F super → next in linearization\n\n\u002F\u002F Linearization order (right-to-left, depth-first):\n\u002F\u002F   new Logger with TimestampedLogger with JsonLogger\n\u002F\u002F   → JsonLogger → TimestampedLogger → Logger → AnyRef → Any\n\u002F\u002F\n\u002F\u002F JsonLogger.log calls super.log → TimestampedLogger.log calls super.log → Logger.log\n\u002F\u002F Result: {\"msg\":\"1234567890 | original message\"}  (JSON wrapping timestamp wrapping plain)\n\nclass App extends TimestampedLogger with JsonLogger:\n  def run(): Unit = log(\"starting\")\n\u002F\u002F App linearization: App → JsonLogger → TimestampedLogger → Logger → AnyRef\n\u002F\u002F 'super' in JsonLogger.log → TimestampedLogger.log (NOT Logger.log directly)\n\n\u002F\u002F ❌ ANTI-PATTERN: relying on trait init order for mutable state\ntrait A:\n  val baseValue: Int = 10\ntrait B extends A:\n  val doubled: Int = baseValue * 2         \u002F\u002F baseValue might be 0 if init order is wrong!\nclass C extends B:\n  override val baseValue = 20              \u002F\u002F B.doubled initialized BEFORE C.baseValue → doubled = 0!\n\n\u002F\u002F ✅ CORRECT: use lazy val or def for derived values\ntrait BSafe extends A:\n  lazy val doubled: Int = baseValue * 2    \u002F\u002F evaluated on first access, after C is fully init\n",[30,434,435,440,445,450,454,464,504,508,522,547,579,583,596,620,641,645,650,655,660,665,670,675,679,698,721,727,733,738,744,754,772,786,812,826,843,848,854,868],{"__ignoreMap":28},[33,436,437],{"class":35,"line":36},[33,438,439],{"class":39},"\u002F\u002F Traits can have concrete methods, fields, and initialization logic.\n",[33,441,442],{"class":35,"line":43},[33,443,444],{"class":39},"\u002F\u002F When multiple traits are mixed in, Scala uses LINEARIZATION to resolve\n",[33,446,447],{"class":35,"line":49},[33,448,449],{"class":39},"\u002F\u002F method dispatch — rightmost trait wins, `super` calls chain up.\n",[33,451,452],{"class":35,"line":110},[33,453,172],{"emptyLinePlaceholder":171},[33,455,456,459,462],{"class":35,"line":156},[33,457,458],{"class":52},"trait",[33,460,461],{"class":59}," Logger",[33,463,107],{"class":52},[33,465,466,468,471,473,476,478,481,483,485,487,489,492,494,497,500,502],{"class":35,"line":168},[33,467,247],{"class":52},[33,469,470],{"class":59}," log",[33,472,64],{"class":63},[33,474,475],{"class":70},"msg",[33,477,98],{"class":63},[33,479,480],{"class":59},"String",[33,482,104],{"class":63},[33,484,74],{"class":52},[33,486,304],{"class":59},[33,488,198],{"class":52},[33,490,491],{"class":63}," println(",[33,493,137],{"class":52},[33,495,496],{"class":140},"\"[LOG] ",[33,498,499],{"class":63},"$msg",[33,501,147],{"class":140},[33,503,165],{"class":63},[33,505,506],{"class":35,"line":175},[33,507,172],{"emptyLinePlaceholder":171},[33,509,510,512,515,518,520],{"class":35,"line":181},[33,511,458],{"class":52},[33,513,514],{"class":59}," TimestampedLogger",[33,516,517],{"class":52}," extends",[33,519,461],{"class":59},[33,521,107],{"class":52},[33,523,524,526,528,530,532,534,536,538,540,542,544],{"class":35,"line":210},[33,525,347],{"class":52},[33,527,350],{"class":52},[33,529,470],{"class":59},[33,531,64],{"class":63},[33,533,475],{"class":70},[33,535,98],{"class":63},[33,537,480],{"class":59},[33,539,104],{"class":63},[33,541,74],{"class":52},[33,543,304],{"class":59},[33,545,546],{"class":52}," =\n",[33,548,549,552,555,557,559,562,564,567,570,572,574,576],{"class":35,"line":233},[33,550,551],{"class":119},"    super",[33,553,554],{"class":63},".log(",[33,556,137],{"class":52},[33,558,147],{"class":140},[33,560,561],{"class":63},"${",[33,563,262],{"class":59},[33,565,566],{"class":63},".currentTimeMillis()}",[33,568,569],{"class":140}," | ",[33,571,499],{"class":63},[33,573,147],{"class":140},[33,575,150],{"class":63},[33,577,578],{"class":39},"\u002F\u002F super → next in linearization\n",[33,580,581],{"class":35,"line":238},[33,582,172],{"emptyLinePlaceholder":171},[33,584,585,587,590,592,594],{"class":35,"line":244},[33,586,458],{"class":52},[33,588,589],{"class":59}," JsonLogger",[33,591,517],{"class":52},[33,593,461],{"class":59},[33,595,107],{"class":52},[33,597,598,600,602,604,606,608,610,612,614,616,618],{"class":35,"line":280},[33,599,347],{"class":52},[33,601,350],{"class":52},[33,603,470],{"class":59},[33,605,64],{"class":63},[33,607,475],{"class":70},[33,609,98],{"class":63},[33,611,480],{"class":59},[33,613,104],{"class":63},[33,615,74],{"class":52},[33,617,304],{"class":59},[33,619,546],{"class":52},[33,621,622,624,626,628,631,633,636,639],{"class":35,"line":285},[33,623,551],{"class":119},[33,625,554],{"class":63},[33,627,137],{"class":52},[33,629,630],{"class":140},"\"\"\"{\"msg\":\"",[33,632,499],{"class":63},[33,634,635],{"class":140},"\"}\"\"\"",[33,637,638],{"class":63},")          ",[33,640,578],{"class":39},[33,642,643],{"class":35,"line":291},[33,644,172],{"emptyLinePlaceholder":171},[33,646,647],{"class":35,"line":315},[33,648,649],{"class":39},"\u002F\u002F Linearization order (right-to-left, depth-first):\n",[33,651,652],{"class":35,"line":327},[33,653,654],{"class":39},"\u002F\u002F   new Logger with TimestampedLogger with JsonLogger\n",[33,656,657],{"class":35,"line":333},[33,658,659],{"class":39},"\u002F\u002F   → JsonLogger → TimestampedLogger → Logger → AnyRef → Any\n",[33,661,662],{"class":35,"line":338},[33,663,664],{"class":39},"\u002F\u002F\n",[33,666,667],{"class":35,"line":344},[33,668,669],{"class":39},"\u002F\u002F JsonLogger.log calls super.log → TimestampedLogger.log calls super.log → Logger.log\n",[33,671,672],{"class":35,"line":384},[33,673,674],{"class":39},"\u002F\u002F Result: {\"msg\":\"1234567890 | original message\"}  (JSON wrapping timestamp wrapping plain)\n",[33,676,677],{"class":35,"line":389},[33,678,172],{"emptyLinePlaceholder":171},[33,680,681,684,687,689,691,694,696],{"class":35,"line":395},[33,682,683],{"class":52},"class",[33,685,686],{"class":59}," App",[33,688,517],{"class":52},[33,690,514],{"class":59},[33,692,693],{"class":52}," with",[33,695,589],{"class":59},[33,697,107],{"class":52},[33,699,700,702,705,707,709,711,713,716,719],{"class":35,"line":420},[33,701,247],{"class":52},[33,703,704],{"class":59}," run",[33,706,299],{"class":63},[33,708,74],{"class":52},[33,710,304],{"class":59},[33,712,198],{"class":52},[33,714,715],{"class":63}," log(",[33,717,718],{"class":140},"\"starting\"",[33,720,165],{"class":63},[33,722,724],{"class":35,"line":723},25,[33,725,726],{"class":39},"\u002F\u002F App linearization: App → JsonLogger → TimestampedLogger → Logger → AnyRef\n",[33,728,730],{"class":35,"line":729},26,[33,731,732],{"class":39},"\u002F\u002F 'super' in JsonLogger.log → TimestampedLogger.log (NOT Logger.log directly)\n",[33,734,736],{"class":35,"line":735},27,[33,737,172],{"emptyLinePlaceholder":171},[33,739,741],{"class":35,"line":740},28,[33,742,743],{"class":39},"\u002F\u002F ❌ ANTI-PATTERN: relying on trait init order for mutable state\n",[33,745,747,749,752],{"class":35,"line":746},29,[33,748,458],{"class":52},[33,750,751],{"class":59}," A",[33,753,107],{"class":52},[33,755,757,760,763,765,767,769],{"class":35,"line":756},30,[33,758,759],{"class":52},"  val",[33,761,762],{"class":70}," baseValue",[33,764,74],{"class":52},[33,766,90],{"class":59},[33,768,198],{"class":52},[33,770,771],{"class":119}," 10\n",[33,773,775,777,780,782,784],{"class":35,"line":774},31,[33,776,458],{"class":52},[33,778,779],{"class":59}," B",[33,781,517],{"class":52},[33,783,751],{"class":59},[33,785,107],{"class":52},[33,787,789,791,794,796,798,800,803,806,809],{"class":35,"line":788},32,[33,790,759],{"class":52},[33,792,793],{"class":70}," doubled",[33,795,74],{"class":52},[33,797,90],{"class":59},[33,799,198],{"class":52},[33,801,802],{"class":63}," baseValue ",[33,804,805],{"class":52},"*",[33,807,808],{"class":119}," 2",[33,810,811],{"class":39},"         \u002F\u002F baseValue might be 0 if init order is wrong!\n",[33,813,815,817,820,822,824],{"class":35,"line":814},33,[33,816,683],{"class":52},[33,818,819],{"class":59}," C",[33,821,517],{"class":52},[33,823,779],{"class":59},[33,825,107],{"class":52},[33,827,829,831,833,835,837,840],{"class":35,"line":828},34,[33,830,347],{"class":52},[33,832,187],{"class":52},[33,834,762],{"class":70},[33,836,198],{"class":52},[33,838,839],{"class":119}," 20",[33,841,842],{"class":39},"              \u002F\u002F B.doubled initialized BEFORE C.baseValue → doubled = 0!\n",[33,844,846],{"class":35,"line":845},35,[33,847,172],{"emptyLinePlaceholder":171},[33,849,851],{"class":35,"line":850},36,[33,852,853],{"class":39},"\u002F\u002F ✅ CORRECT: use lazy val or def for derived values\n",[33,855,857,859,862,864,866],{"class":35,"line":856},37,[33,858,458],{"class":52},[33,860,861],{"class":59}," BSafe",[33,863,517],{"class":52},[33,865,751],{"class":59},[33,867,107],{"class":52},[33,869,871,874,876,878,880,882,884,886,888,890],{"class":35,"line":870},38,[33,872,873],{"class":52},"  lazy",[33,875,187],{"class":52},[33,877,793],{"class":70},[33,879,74],{"class":52},[33,881,90],{"class":59},[33,883,198],{"class":52},[33,885,802],{"class":63},[33,887,805],{"class":52},[33,889,808],{"class":119},[33,891,892],{"class":39},"    \u002F\u002F evaluated on first access, after C is fully init\n",[15,894,896],{"id":895},"self-types-dependency-without-inheritance","Self Types — Dependency Without Inheritance",[20,898,899],{"language":22},[24,900,902],{"className":26,"code":901,"language":22,"meta":28,"style":28},"\u002F\u002F Self type: \"I require this trait to be mixed in, but I don't inherit from it.\"\n\u002F\u002F Unlike `extends`, self type does NOT make the trait a subtype.\n\u002F\u002F The compiler ensures any class mixing in this trait ALSO has the required trait.\n\ntrait HasConfig:\n  def config: Config\n\ntrait HasDatabase:\n  def db: Database\n\n\u002F\u002F Service requires both Config and Database at mixin time\ntrait UserService:\n  this: HasConfig & HasDatabase =>          \u002F\u002F intersection self type (Scala 3)\n  def findUser(id: Long): Option[User] =\n    db.query(config.userQuery, id)          \u002F\u002F can call config and db — guaranteed present\n\n\u002F\u002F ❌ class BadService extends UserService  \u002F\u002F ERROR: doesn't have HasConfig & HasDatabase\n\u002F\u002F ✅ Must mix in all required traits:\nclass ProdService extends UserService with HasConfig with HasDatabase:\n  val config: Config = loadConfig()\n  val db: Database = Database.connect(config.dbUrl)\n\n\u002F\u002F Use self types for:\n\u002F\u002F   - Dependency injection (compile-time enforcement)\n\u002F\u002F   - Cake pattern — traits that require other traits from the same \"cake\"\n\u002F\u002F   - Avoiding inheritance when you only need access to methods, not IS-A relationship\n",[30,903,904,909,914,919,923,932,944,948,957,969,973,978,987,1007,1043,1051,1055,1063,1068,1089,1105,1123,1127,1132,1137,1142],{"__ignoreMap":28},[33,905,906],{"class":35,"line":36},[33,907,908],{"class":39},"\u002F\u002F Self type: \"I require this trait to be mixed in, but I don't inherit from it.\"\n",[33,910,911],{"class":35,"line":43},[33,912,913],{"class":39},"\u002F\u002F Unlike `extends`, self type does NOT make the trait a subtype.\n",[33,915,916],{"class":35,"line":49},[33,917,918],{"class":39},"\u002F\u002F The compiler ensures any class mixing in this trait ALSO has the required trait.\n",[33,920,921],{"class":35,"line":110},[33,922,172],{"emptyLinePlaceholder":171},[33,924,925,927,930],{"class":35,"line":156},[33,926,458],{"class":52},[33,928,929],{"class":59}," HasConfig",[33,931,107],{"class":52},[33,933,934,936,939,941],{"class":35,"line":168},[33,935,247],{"class":52},[33,937,938],{"class":59}," config",[33,940,74],{"class":52},[33,942,943],{"class":59}," Config\n",[33,945,946],{"class":35,"line":175},[33,947,172],{"emptyLinePlaceholder":171},[33,949,950,952,955],{"class":35,"line":181},[33,951,458],{"class":52},[33,953,954],{"class":59}," HasDatabase",[33,956,107],{"class":52},[33,958,959,961,964,966],{"class":35,"line":210},[33,960,247],{"class":52},[33,962,963],{"class":59}," db",[33,965,74],{"class":52},[33,967,968],{"class":59}," Database\n",[33,970,971],{"class":35,"line":233},[33,972,172],{"emptyLinePlaceholder":171},[33,974,975],{"class":35,"line":238},[33,976,977],{"class":39},"\u002F\u002F Service requires both Config and Database at mixin time\n",[33,979,980,982,985],{"class":35,"line":244},[33,981,458],{"class":52},[33,983,984],{"class":59}," UserService",[33,986,107],{"class":52},[33,988,989,992,994,996,999,1001,1004],{"class":35,"line":280},[33,990,991],{"class":119},"  this",[33,993,74],{"class":52},[33,995,929],{"class":59},[33,997,998],{"class":52}," &",[33,1000,954],{"class":59},[33,1002,1003],{"class":52}," =>",[33,1005,1006],{"class":39},"          \u002F\u002F intersection self type (Scala 3)\n",[33,1008,1009,1011,1014,1016,1019,1021,1024,1026,1028,1031,1034,1037,1040],{"class":35,"line":285},[33,1010,247],{"class":52},[33,1012,1013],{"class":59}," findUser",[33,1015,64],{"class":63},[33,1017,1018],{"class":70},"id",[33,1020,98],{"class":63},[33,1022,1023],{"class":59},"Long",[33,1025,104],{"class":63},[33,1027,74],{"class":52},[33,1029,1030],{"class":59}," Option",[33,1032,1033],{"class":63},"[",[33,1035,1036],{"class":59},"User",[33,1038,1039],{"class":63},"] ",[33,1041,1042],{"class":52},"=\n",[33,1044,1045,1048],{"class":35,"line":291},[33,1046,1047],{"class":63},"    db.query(config.userQuery, id)          ",[33,1049,1050],{"class":39},"\u002F\u002F can call config and db — guaranteed present\n",[33,1052,1053],{"class":35,"line":315},[33,1054,172],{"emptyLinePlaceholder":171},[33,1056,1057,1060],{"class":35,"line":327},[33,1058,1059],{"class":39},"\u002F\u002F ❌ class BadService extends UserService",[33,1061,1062],{"class":39},"  \u002F\u002F ERROR: doesn't have HasConfig & HasDatabase\n",[33,1064,1065],{"class":35,"line":333},[33,1066,1067],{"class":39},"\u002F\u002F ✅ Must mix in all required traits:\n",[33,1069,1070,1072,1075,1077,1079,1081,1083,1085,1087],{"class":35,"line":338},[33,1071,683],{"class":52},[33,1073,1074],{"class":59}," ProdService",[33,1076,517],{"class":52},[33,1078,984],{"class":59},[33,1080,693],{"class":52},[33,1082,929],{"class":59},[33,1084,693],{"class":52},[33,1086,954],{"class":59},[33,1088,107],{"class":52},[33,1090,1091,1093,1095,1097,1100,1102],{"class":35,"line":344},[33,1092,759],{"class":52},[33,1094,938],{"class":70},[33,1096,74],{"class":52},[33,1098,1099],{"class":59}," Config",[33,1101,198],{"class":52},[33,1103,1104],{"class":63}," loadConfig()\n",[33,1106,1107,1109,1111,1113,1116,1118,1120],{"class":35,"line":384},[33,1108,759],{"class":52},[33,1110,963],{"class":70},[33,1112,74],{"class":52},[33,1114,1115],{"class":59}," Database",[33,1117,198],{"class":52},[33,1119,1115],{"class":59},[33,1121,1122],{"class":63},".connect(config.dbUrl)\n",[33,1124,1125],{"class":35,"line":389},[33,1126,172],{"emptyLinePlaceholder":171},[33,1128,1129],{"class":35,"line":395},[33,1130,1131],{"class":39},"\u002F\u002F Use self types for:\n",[33,1133,1134],{"class":35,"line":420},[33,1135,1136],{"class":39},"\u002F\u002F   - Dependency injection (compile-time enforcement)\n",[33,1138,1139],{"class":35,"line":723},[33,1140,1141],{"class":39},"\u002F\u002F   - Cake pattern — traits that require other traits from the same \"cake\"\n",[33,1143,1144],{"class":35,"line":729},[33,1145,1146],{"class":39},"\u002F\u002F   - Avoiding inheritance when you only need access to methods, not IS-A relationship\n",[15,1148,1150,1153],{"id":1149},"open-classes-sealed-hierarchies-scala-3",[30,1151,1152],{},"open"," Classes & Sealed Hierarchies — Scala 3",[20,1155,1156],{"language":22},[24,1157,1159],{"className":26,"code":1158,"language":22,"meta":28,"style":28},"\u002F\u002F Scala 3: classes are CLOSED by default (cannot be subclassed outside the file).\n\u002F\u002F Must explicitly mark `open` to allow inheritance.\n\nopen class BaseRepository[T](val tableName: String):\n  def find(id: Long): Option[T] = ???      \u002F\u002F subclasses CAN override\n  def save(t: T): Unit = ???\n\nclass UserRepository extends BaseRepository[User](\"users\"):\n  override def find(id: Long): Option[User] = ???  \u002F\u002F open allows this\n\n\u002F\u002F sealed: all subtypes MUST be in the same file → exhaustiveness checking\nsealed trait Result[+T]:\n  def map[U](f: T => U): Result[U] = this match\n    case Ok(v)    => Ok(f(v))\n    case Err(msg) => Err(msg)               \u002F\u002F sealed → compiler knows these are the only cases\n\nobject Result:\n  case class Ok[T](value: T) extends Result[T]\n  case class Err(msg: String) extends Result[Nothing]  \u002F\u002F Nothing is bottom type → covariant OK\n\n\u002F\u002F Adding a new subtype (e.g., Pending) → ALL match expressions get compile errors\n\u002F\u002F This is the strongest form of \"closed for modification\" — the compiler enforces it.\n",[30,1160,1161,1166,1171,1175,1205,1241,1268,1272,1296,1331,1335,1340,1363,1409,1428,1448,1452,1461,1498,1531,1535,1540],{"__ignoreMap":28},[33,1162,1163],{"class":35,"line":36},[33,1164,1165],{"class":39},"\u002F\u002F Scala 3: classes are CLOSED by default (cannot be subclassed outside the file).\n",[33,1167,1168],{"class":35,"line":43},[33,1169,1170],{"class":39},"\u002F\u002F Must explicitly mark `open` to allow inheritance.\n",[33,1172,1173],{"class":35,"line":49},[33,1174,172],{"emptyLinePlaceholder":171},[33,1176,1177,1179,1181,1184,1186,1189,1192,1194,1197,1199,1201,1203],{"class":35,"line":110},[33,1178,1152],{"class":52},[33,1180,56],{"class":52},[33,1182,1183],{"class":59}," BaseRepository",[33,1185,1033],{"class":63},[33,1187,1188],{"class":59},"T",[33,1190,1191],{"class":63},"](",[33,1193,67],{"class":52},[33,1195,1196],{"class":70}," tableName",[33,1198,74],{"class":52},[33,1200,77],{"class":59},[33,1202,104],{"class":63},[33,1204,107],{"class":52},[33,1206,1207,1209,1212,1214,1216,1218,1220,1222,1224,1226,1228,1230,1232,1235,1238],{"class":35,"line":156},[33,1208,247],{"class":52},[33,1210,1211],{"class":59}," find",[33,1213,64],{"class":63},[33,1215,1018],{"class":70},[33,1217,98],{"class":63},[33,1219,1023],{"class":59},[33,1221,104],{"class":63},[33,1223,74],{"class":52},[33,1225,1030],{"class":59},[33,1227,1033],{"class":63},[33,1229,1188],{"class":59},[33,1231,1039],{"class":63},[33,1233,1234],{"class":52},"=",[33,1236,1237],{"class":52}," ???",[33,1239,1240],{"class":39},"      \u002F\u002F subclasses CAN override\n",[33,1242,1243,1245,1248,1250,1253,1255,1257,1259,1261,1263,1265],{"class":35,"line":168},[33,1244,247],{"class":52},[33,1246,1247],{"class":59}," save",[33,1249,64],{"class":63},[33,1251,1252],{"class":70},"t",[33,1254,98],{"class":63},[33,1256,1188],{"class":59},[33,1258,104],{"class":63},[33,1260,74],{"class":52},[33,1262,304],{"class":59},[33,1264,198],{"class":52},[33,1266,1267],{"class":52}," ???\n",[33,1269,1270],{"class":35,"line":175},[33,1271,172],{"emptyLinePlaceholder":171},[33,1273,1274,1276,1279,1281,1283,1285,1287,1289,1292,1294],{"class":35,"line":181},[33,1275,683],{"class":52},[33,1277,1278],{"class":59}," UserRepository",[33,1280,517],{"class":52},[33,1282,1183],{"class":59},[33,1284,1033],{"class":63},[33,1286,1036],{"class":59},[33,1288,1191],{"class":63},[33,1290,1291],{"class":140},"\"users\"",[33,1293,104],{"class":63},[33,1295,107],{"class":52},[33,1297,1298,1300,1302,1304,1306,1308,1310,1312,1314,1316,1318,1320,1322,1324,1326,1328],{"class":35,"line":210},[33,1299,347],{"class":52},[33,1301,350],{"class":52},[33,1303,1211],{"class":59},[33,1305,64],{"class":63},[33,1307,1018],{"class":70},[33,1309,98],{"class":63},[33,1311,1023],{"class":59},[33,1313,104],{"class":63},[33,1315,74],{"class":52},[33,1317,1030],{"class":59},[33,1319,1033],{"class":63},[33,1321,1036],{"class":59},[33,1323,1039],{"class":63},[33,1325,1234],{"class":52},[33,1327,1237],{"class":52},[33,1329,1330],{"class":39},"  \u002F\u002F open allows this\n",[33,1332,1333],{"class":35,"line":233},[33,1334,172],{"emptyLinePlaceholder":171},[33,1336,1337],{"class":35,"line":238},[33,1338,1339],{"class":39},"\u002F\u002F sealed: all subtypes MUST be in the same file → exhaustiveness checking\n",[33,1341,1342,1345,1348,1351,1353,1356,1358,1361],{"class":35,"line":244},[33,1343,1344],{"class":52},"sealed",[33,1346,1347],{"class":52}," trait",[33,1349,1350],{"class":59}," Result",[33,1352,1033],{"class":63},[33,1354,1355],{"class":52},"+",[33,1357,1188],{"class":59},[33,1359,1360],{"class":63},"]",[33,1362,107],{"class":52},[33,1364,1365,1367,1370,1372,1375,1377,1380,1382,1384,1386,1389,1391,1393,1395,1397,1399,1401,1403,1406],{"class":35,"line":280},[33,1366,247],{"class":52},[33,1368,1369],{"class":59}," map",[33,1371,1033],{"class":63},[33,1373,1374],{"class":59},"U",[33,1376,1191],{"class":63},[33,1378,1379],{"class":70},"f",[33,1381,98],{"class":63},[33,1383,1188],{"class":59},[33,1385,1003],{"class":52},[33,1387,1388],{"class":59}," U",[33,1390,104],{"class":63},[33,1392,74],{"class":52},[33,1394,1350],{"class":59},[33,1396,1033],{"class":63},[33,1398,1374],{"class":59},[33,1400,1039],{"class":63},[33,1402,1234],{"class":52},[33,1404,1405],{"class":119}," this",[33,1407,1408],{"class":52}," match\n",[33,1410,1411,1414,1417,1420,1423,1425],{"class":35,"line":285},[33,1412,1413],{"class":52},"    case",[33,1415,1416],{"class":59}," Ok",[33,1418,1419],{"class":63},"(v)    ",[33,1421,1422],{"class":52},"=>",[33,1424,1416],{"class":59},[33,1426,1427],{"class":63},"(f(v))\n",[33,1429,1430,1432,1435,1438,1440,1442,1445],{"class":35,"line":291},[33,1431,1413],{"class":52},[33,1433,1434],{"class":59}," Err",[33,1436,1437],{"class":63},"(msg) ",[33,1439,1422],{"class":52},[33,1441,1434],{"class":59},[33,1443,1444],{"class":63},"(msg)               ",[33,1446,1447],{"class":39},"\u002F\u002F sealed → compiler knows these are the only cases\n",[33,1449,1450],{"class":35,"line":315},[33,1451,172],{"emptyLinePlaceholder":171},[33,1453,1454,1457,1459],{"class":35,"line":327},[33,1455,1456],{"class":52},"object",[33,1458,1350],{"class":59},[33,1460,107],{"class":52},[33,1462,1463,1466,1468,1470,1472,1474,1476,1479,1481,1483,1486,1489,1491,1493,1495],{"class":35,"line":333},[33,1464,1465],{"class":52},"  case",[33,1467,56],{"class":52},[33,1469,1416],{"class":59},[33,1471,1033],{"class":63},[33,1473,1188],{"class":59},[33,1475,1191],{"class":63},[33,1477,1478],{"class":70},"value",[33,1480,98],{"class":63},[33,1482,1188],{"class":59},[33,1484,1485],{"class":63},") ",[33,1487,1488],{"class":52},"extends",[33,1490,1350],{"class":59},[33,1492,1033],{"class":63},[33,1494,1188],{"class":59},[33,1496,1497],{"class":63},"]\n",[33,1499,1500,1502,1504,1506,1508,1510,1512,1514,1516,1518,1520,1522,1525,1528],{"class":35,"line":338},[33,1501,1465],{"class":52},[33,1503,56],{"class":52},[33,1505,1434],{"class":59},[33,1507,64],{"class":63},[33,1509,475],{"class":70},[33,1511,98],{"class":63},[33,1513,480],{"class":59},[33,1515,1485],{"class":63},[33,1517,1488],{"class":52},[33,1519,1350],{"class":59},[33,1521,1033],{"class":63},[33,1523,1524],{"class":59},"Nothing",[33,1526,1527],{"class":63},"]  ",[33,1529,1530],{"class":39},"\u002F\u002F Nothing is bottom type → covariant OK\n",[33,1532,1533],{"class":35,"line":344},[33,1534,172],{"emptyLinePlaceholder":171},[33,1536,1537],{"class":35,"line":384},[33,1538,1539],{"class":39},"\u002F\u002F Adding a new subtype (e.g., Pending) → ALL match expressions get compile errors\n",[33,1541,1542],{"class":35,"line":389},[33,1543,1544],{"class":39},"\u002F\u002F This is the strongest form of \"closed for modification\" — the compiler enforces it.\n",[15,1546,1548,1549],{"id":1547},"universal-equality-canequal","Universal Equality & ",[30,1550,1551],{},"canEqual",[20,1553,1554],{"language":22},[24,1555,1557],{"className":26,"code":1556,"language":22,"meta":28,"style":28},"\u002F\u002F Scala 3: `==` calls `equals()`. For case classes, equals is auto-generated (field-by-field).\n\u002F\u002F For regular classes, you must override equals yourself — and do it correctly.\n\nfinal class Money(val cents: Long) extends Equals:\n  override def equals(other: Any): Boolean = other match\n    case that: Money =>\n      that.canEqual(this) && this.cents == that.cents\n    case _ => false\n\n  \u002F\u002F canEqual: allows subclasses to opt out of equality with parent\n  override def canEqual(other: Any): Boolean = other.isInstanceOf[Money]\n\n  \u002F\u002F hashCode MUST be consistent with equals — same cents → same hashCode\n  override def hashCode: Int = cents.hashCode\n\n  \u002F\u002F Scala 3: derive equals\u002FhashCode via @alpha or use case classes instead\n\u002F\u002F Money(100) == Money(100) → true (value equality, not reference)\n\n\u002F\u002F ❌ ANTI-PATTERN: equals without hashCode (breaks HashMap\u002FHashSet)\n\u002F\u002F If a.equals(b) then a.hashCode must == b.hashCode — ALWAYS.\n\u002F\u002F Case classes get this right for free. Prefer case classes for value types.\n\n\u002F\u002F Scala 3: `==` is universal equality. `eq` is reference equality (only for AnyRef).\n\u002F\u002F   Some(1) == Some(1)    → true  (case class equals)\n\u002F\u002F   Some(1) eq Some(1)    → false (different object instances)\n",[30,1558,1559,1564,1569,1573,1602,1636,1651,1675,1687,1691,1696,1733,1737,1742,1760,1764,1769,1774,1778,1783,1788,1793,1797,1802,1807],{"__ignoreMap":28},[33,1560,1561],{"class":35,"line":36},[33,1562,1563],{"class":39},"\u002F\u002F Scala 3: `==` calls `equals()`. For case classes, equals is auto-generated (field-by-field).\n",[33,1565,1566],{"class":35,"line":43},[33,1567,1568],{"class":39},"\u002F\u002F For regular classes, you must override equals yourself — and do it correctly.\n",[33,1570,1571],{"class":35,"line":49},[33,1572,172],{"emptyLinePlaceholder":171},[33,1574,1575,1577,1579,1582,1584,1586,1589,1591,1593,1595,1597,1600],{"class":35,"line":110},[33,1576,53],{"class":52},[33,1578,56],{"class":52},[33,1580,1581],{"class":59}," Money",[33,1583,64],{"class":63},[33,1585,67],{"class":52},[33,1587,1588],{"class":70}," cents",[33,1590,74],{"class":52},[33,1592,195],{"class":59},[33,1594,1485],{"class":63},[33,1596,1488],{"class":52},[33,1598,1599],{"class":59}," Equals",[33,1601,107],{"class":52},[33,1603,1604,1606,1608,1611,1613,1616,1618,1621,1623,1625,1628,1630,1633],{"class":35,"line":156},[33,1605,347],{"class":52},[33,1607,350],{"class":52},[33,1609,1610],{"class":59}," equals",[33,1612,64],{"class":63},[33,1614,1615],{"class":70},"other",[33,1617,98],{"class":63},[33,1619,1620],{"class":59},"Any",[33,1622,104],{"class":63},[33,1624,74],{"class":52},[33,1626,1627],{"class":59}," Boolean",[33,1629,198],{"class":52},[33,1631,1632],{"class":63}," other ",[33,1634,1635],{"class":52},"match\n",[33,1637,1638,1640,1643,1645,1648],{"class":35,"line":168},[33,1639,1413],{"class":52},[33,1641,1642],{"class":70}," that",[33,1644,98],{"class":63},[33,1646,1647],{"class":59},"Money",[33,1649,1650],{"class":52}," =>\n",[33,1652,1653,1656,1659,1661,1664,1666,1669,1672],{"class":35,"line":175},[33,1654,1655],{"class":63},"      that.canEqual(",[33,1657,1658],{"class":119},"this",[33,1660,1485],{"class":63},[33,1662,1663],{"class":52},"&&",[33,1665,1405],{"class":119},[33,1667,1668],{"class":63},".cents ",[33,1670,1671],{"class":52},"==",[33,1673,1674],{"class":63}," that.cents\n",[33,1676,1677,1679,1682,1684],{"class":35,"line":181},[33,1678,1413],{"class":52},[33,1680,1681],{"class":63}," _ ",[33,1683,1422],{"class":52},[33,1685,1686],{"class":119}," false\n",[33,1688,1689],{"class":35,"line":210},[33,1690,172],{"emptyLinePlaceholder":171},[33,1692,1693],{"class":35,"line":233},[33,1694,1695],{"class":39},"  \u002F\u002F canEqual: allows subclasses to opt out of equality with parent\n",[33,1697,1698,1700,1702,1705,1707,1709,1711,1713,1715,1717,1719,1721,1724,1727,1729,1731],{"class":35,"line":238},[33,1699,347],{"class":52},[33,1701,350],{"class":52},[33,1703,1704],{"class":59}," canEqual",[33,1706,64],{"class":63},[33,1708,1615],{"class":70},[33,1710,98],{"class":63},[33,1712,1620],{"class":59},[33,1714,104],{"class":63},[33,1716,74],{"class":52},[33,1718,1627],{"class":59},[33,1720,198],{"class":52},[33,1722,1723],{"class":63}," other.",[33,1725,1726],{"class":119},"isInstanceOf",[33,1728,1033],{"class":63},[33,1730,1647],{"class":59},[33,1732,1497],{"class":63},[33,1734,1735],{"class":35,"line":244},[33,1736,172],{"emptyLinePlaceholder":171},[33,1738,1739],{"class":35,"line":280},[33,1740,1741],{"class":39},"  \u002F\u002F hashCode MUST be consistent with equals — same cents → same hashCode\n",[33,1743,1744,1746,1748,1751,1753,1755,1757],{"class":35,"line":285},[33,1745,347],{"class":52},[33,1747,350],{"class":52},[33,1749,1750],{"class":59}," hashCode",[33,1752,74],{"class":52},[33,1754,90],{"class":59},[33,1756,198],{"class":52},[33,1758,1759],{"class":63}," cents.hashCode\n",[33,1761,1762],{"class":35,"line":291},[33,1763,172],{"emptyLinePlaceholder":171},[33,1765,1766],{"class":35,"line":315},[33,1767,1768],{"class":39},"  \u002F\u002F Scala 3: derive equals\u002FhashCode via @alpha or use case classes instead\n",[33,1770,1771],{"class":35,"line":327},[33,1772,1773],{"class":39},"\u002F\u002F Money(100) == Money(100) → true (value equality, not reference)\n",[33,1775,1776],{"class":35,"line":333},[33,1777,172],{"emptyLinePlaceholder":171},[33,1779,1780],{"class":35,"line":338},[33,1781,1782],{"class":39},"\u002F\u002F ❌ ANTI-PATTERN: equals without hashCode (breaks HashMap\u002FHashSet)\n",[33,1784,1785],{"class":35,"line":344},[33,1786,1787],{"class":39},"\u002F\u002F If a.equals(b) then a.hashCode must == b.hashCode — ALWAYS.\n",[33,1789,1790],{"class":35,"line":384},[33,1791,1792],{"class":39},"\u002F\u002F Case classes get this right for free. Prefer case classes for value types.\n",[33,1794,1795],{"class":35,"line":389},[33,1796,172],{"emptyLinePlaceholder":171},[33,1798,1799],{"class":35,"line":395},[33,1800,1801],{"class":39},"\u002F\u002F Scala 3: `==` is universal equality. `eq` is reference equality (only for AnyRef).\n",[33,1803,1804],{"class":35,"line":420},[33,1805,1806],{"class":39},"\u002F\u002F   Some(1) == Some(1)    → true  (case class equals)\n",[33,1808,1809],{"class":35,"line":723},[33,1810,1811],{"class":39},"\u002F\u002F   Some(1) eq Some(1)    → false (different object instances)\n",[15,1813,1815],{"id":1814},"companion-objects-factory-patterns","Companion Objects & Factory Patterns",[20,1817,1818],{"language":22},[24,1819,1821],{"className":26,"code":1820,"language":22,"meta":28,"style":28},"\u002F\u002F Companion: class + object with same name in same file.\n\u002F\u002F Object can access private members of class and vice versa.\n\nfinal class Connection private (val url: String, val timeoutMs: Int):\n  def execute(query: String): Result[Array[Byte]] = ???\n\nobject Connection:\n  \u002F\u002F Smart constructor — validates input, returns Either for failure\n  def create(url: String, timeoutMs: Int = 5000): Either[String, Connection] =\n    if url.isBlank then Left(\"URL must not be empty\")\n    else if timeoutMs \u003C 0 then Left(\"timeout must be non-negative\")\n    else Right(new Connection(url, timeoutMs))  \u002F\u002F private constructor — only companion can call\n\n  \u002F\u002F Factory from config — common pattern for dependency injection\n  def fromConfig(cfg: Config): Connection =\n    new Connection(cfg.getString(\"db.url\"), cfg.getInt(\"db.timeout\"))\n\n  \u002F\u002F Apply with default — sugar for Connection(...)\n  def apply(url: String): Connection = new Connection(url, 5000)\n\n\u002F\u002F Usage:\nConnection.create(\"jdbc:postgres:\u002F\u002F...\") match\n  case Right(conn) => conn.execute(\"SELECT 1\")\n  case Left(err)   => println(s\"Failed: $err\")\n\n\u002F\u002F Private constructor: `new Connection(...)` is illegal outside the companion\n\u002F\u002F This is the standard \"smart constructor\" pattern — control all instantiation.\n",[30,1822,1823,1828,1833,1837,1875,1914,1918,1926,1931,1982,2003,2031,2051,2055,2060,2084,2106,2110,2115,2151,2155,2160,2174,2193,2218,2222,2227],{"__ignoreMap":28},[33,1824,1825],{"class":35,"line":36},[33,1826,1827],{"class":39},"\u002F\u002F Companion: class + object with same name in same file.\n",[33,1829,1830],{"class":35,"line":43},[33,1831,1832],{"class":39},"\u002F\u002F Object can access private members of class and vice versa.\n",[33,1834,1835],{"class":35,"line":49},[33,1836,172],{"emptyLinePlaceholder":171},[33,1838,1839,1841,1843,1846,1849,1851,1853,1856,1858,1860,1862,1864,1867,1869,1871,1873],{"class":35,"line":110},[33,1840,53],{"class":52},[33,1842,56],{"class":52},[33,1844,1845],{"class":59}," Connection",[33,1847,1848],{"class":52}," private",[33,1850,259],{"class":63},[33,1852,67],{"class":52},[33,1854,1855],{"class":70}," url",[33,1857,74],{"class":52},[33,1859,77],{"class":59},[33,1861,80],{"class":63},[33,1863,67],{"class":52},[33,1865,1866],{"class":70}," timeoutMs",[33,1868,74],{"class":52},[33,1870,90],{"class":59},[33,1872,104],{"class":63},[33,1874,107],{"class":52},[33,1876,1877,1879,1882,1884,1887,1889,1891,1893,1895,1897,1899,1902,1904,1907,1910,1912],{"class":35,"line":156},[33,1878,247],{"class":52},[33,1880,1881],{"class":59}," execute",[33,1883,64],{"class":63},[33,1885,1886],{"class":70},"query",[33,1888,98],{"class":63},[33,1890,480],{"class":59},[33,1892,104],{"class":63},[33,1894,74],{"class":52},[33,1896,1350],{"class":59},[33,1898,1033],{"class":63},[33,1900,1901],{"class":59},"Array",[33,1903,1033],{"class":63},[33,1905,1906],{"class":59},"Byte",[33,1908,1909],{"class":63},"]] ",[33,1911,1234],{"class":52},[33,1913,1267],{"class":52},[33,1915,1916],{"class":35,"line":168},[33,1917,172],{"emptyLinePlaceholder":171},[33,1919,1920,1922,1924],{"class":35,"line":175},[33,1921,1456],{"class":52},[33,1923,1845],{"class":59},[33,1925,107],{"class":52},[33,1927,1928],{"class":35,"line":181},[33,1929,1930],{"class":39},"  \u002F\u002F Smart constructor — validates input, returns Either for failure\n",[33,1932,1933,1935,1938,1940,1943,1945,1947,1949,1952,1954,1957,1959,1962,1964,1966,1969,1971,1973,1975,1978,1980],{"class":35,"line":210},[33,1934,247],{"class":52},[33,1936,1937],{"class":59}," create",[33,1939,64],{"class":63},[33,1941,1942],{"class":70},"url",[33,1944,98],{"class":63},[33,1946,480],{"class":59},[33,1948,80],{"class":63},[33,1950,1951],{"class":70},"timeoutMs",[33,1953,98],{"class":63},[33,1955,1956],{"class":59},"Int",[33,1958,198],{"class":52},[33,1960,1961],{"class":119}," 5000",[33,1963,104],{"class":63},[33,1965,74],{"class":52},[33,1967,1968],{"class":59}," Either",[33,1970,1033],{"class":63},[33,1972,480],{"class":59},[33,1974,80],{"class":63},[33,1976,1977],{"class":59},"Connection",[33,1979,1039],{"class":63},[33,1981,1042],{"class":52},[33,1983,1984,1987,1990,1993,1996,1998,2001],{"class":35,"line":233},[33,1985,1986],{"class":52},"    if",[33,1988,1989],{"class":63}," url.isBlank ",[33,1991,1992],{"class":52},"then",[33,1994,1995],{"class":59}," Left",[33,1997,64],{"class":63},[33,1999,2000],{"class":140},"\"URL must not be empty\"",[33,2002,165],{"class":63},[33,2004,2005,2008,2011,2014,2017,2019,2022,2024,2026,2029],{"class":35,"line":238},[33,2006,2007],{"class":52},"    else",[33,2009,2010],{"class":52}," if",[33,2012,2013],{"class":63}," timeoutMs ",[33,2015,2016],{"class":52},"\u003C",[33,2018,120],{"class":119},[33,2020,2021],{"class":52}," then",[33,2023,1995],{"class":59},[33,2025,64],{"class":63},[33,2027,2028],{"class":140},"\"timeout must be non-negative\"",[33,2030,165],{"class":63},[33,2032,2033,2035,2038,2040,2043,2045,2048],{"class":35,"line":244},[33,2034,2007],{"class":52},[33,2036,2037],{"class":59}," Right",[33,2039,64],{"class":63},[33,2041,2042],{"class":52},"new",[33,2044,1845],{"class":59},[33,2046,2047],{"class":63},"(url, timeoutMs))  ",[33,2049,2050],{"class":39},"\u002F\u002F private constructor — only companion can call\n",[33,2052,2053],{"class":35,"line":280},[33,2054,172],{"emptyLinePlaceholder":171},[33,2056,2057],{"class":35,"line":285},[33,2058,2059],{"class":39},"  \u002F\u002F Factory from config — common pattern for dependency injection\n",[33,2061,2062,2064,2067,2069,2072,2074,2076,2078,2080,2082],{"class":35,"line":291},[33,2063,247],{"class":52},[33,2065,2066],{"class":59}," fromConfig",[33,2068,64],{"class":63},[33,2070,2071],{"class":70},"cfg",[33,2073,98],{"class":63},[33,2075,101],{"class":59},[33,2077,104],{"class":63},[33,2079,74],{"class":52},[33,2081,1845],{"class":59},[33,2083,546],{"class":52},[33,2085,2086,2089,2091,2094,2097,2100,2103],{"class":35,"line":315},[33,2087,2088],{"class":52},"    new",[33,2090,1845],{"class":59},[33,2092,2093],{"class":63},"(cfg.getString(",[33,2095,2096],{"class":140},"\"db.url\"",[33,2098,2099],{"class":63},"), cfg.getInt(",[33,2101,2102],{"class":140},"\"db.timeout\"",[33,2104,2105],{"class":63},"))\n",[33,2107,2108],{"class":35,"line":327},[33,2109,172],{"emptyLinePlaceholder":171},[33,2111,2112],{"class":35,"line":333},[33,2113,2114],{"class":39},"  \u002F\u002F Apply with default — sugar for Connection(...)\n",[33,2116,2117,2119,2122,2124,2126,2128,2130,2132,2134,2136,2138,2141,2143,2146,2149],{"class":35,"line":338},[33,2118,247],{"class":52},[33,2120,2121],{"class":59}," apply",[33,2123,64],{"class":63},[33,2125,1942],{"class":70},[33,2127,98],{"class":63},[33,2129,480],{"class":59},[33,2131,104],{"class":63},[33,2133,74],{"class":52},[33,2135,1845],{"class":59},[33,2137,198],{"class":52},[33,2139,2140],{"class":52}," new",[33,2142,1845],{"class":59},[33,2144,2145],{"class":63},"(url, ",[33,2147,2148],{"class":119},"5000",[33,2150,165],{"class":63},[33,2152,2153],{"class":35,"line":344},[33,2154,172],{"emptyLinePlaceholder":171},[33,2156,2157],{"class":35,"line":384},[33,2158,2159],{"class":39},"\u002F\u002F Usage:\n",[33,2161,2162,2164,2167,2170,2172],{"class":35,"line":389},[33,2163,1977],{"class":59},[33,2165,2166],{"class":63},".create(",[33,2168,2169],{"class":140},"\"jdbc:postgres:\u002F\u002F...\"",[33,2171,1485],{"class":63},[33,2173,1635],{"class":52},[33,2175,2176,2178,2180,2183,2185,2188,2191],{"class":35,"line":395},[33,2177,1465],{"class":52},[33,2179,2037],{"class":59},[33,2181,2182],{"class":63},"(conn) ",[33,2184,1422],{"class":52},[33,2186,2187],{"class":63}," conn.execute(",[33,2189,2190],{"class":140},"\"SELECT 1\"",[33,2192,165],{"class":63},[33,2194,2195,2197,2199,2202,2204,2206,2208,2211,2214,2216],{"class":35,"line":420},[33,2196,1465],{"class":52},[33,2198,1995],{"class":59},[33,2200,2201],{"class":63},"(err)   ",[33,2203,1422],{"class":52},[33,2205,491],{"class":63},[33,2207,137],{"class":52},[33,2209,2210],{"class":140},"\"Failed: ",[33,2212,2213],{"class":63},"$err",[33,2215,147],{"class":140},[33,2217,165],{"class":63},[33,2219,2220],{"class":35,"line":723},[33,2221,172],{"emptyLinePlaceholder":171},[33,2223,2224],{"class":35,"line":729},[33,2225,2226],{"class":39},"\u002F\u002F Private constructor: `new Connection(...)` is illegal outside the companion\n",[33,2228,2229],{"class":35,"line":735},[33,2230,2231],{"class":39},"\u002F\u002F This is the standard \"smart constructor\" pattern — control all instantiation.\n",[15,2233,2235],{"id":2234},"abstract-types-path-dependent-types","Abstract Types & Path-Dependent Types",[20,2237,2238],{"language":22},[24,2239,2241],{"className":26,"code":2240,"language":22,"meta":28,"style":28},"\u002F\u002F Abstract type member — declared in trait, implemented in subclass.\n\u002F\u002F Unlike generics, the type is \"attached\" to the instance, not the call site.\n\ntrait Database:\n  type Conn                            \u002F\u002F abstract type — each DB impl defines its own connection type\n  def connect(): Conn\n  def query(c: Conn, sql: String): Result[Array[Byte]]\n\nclass PostgresDB extends Database:\n  type Conn = java.sql.Connection      \u002F\u002F concrete: Conn is a JDBC Connection\n  def connect(): Conn = DriverManager.getConnection(url)\n  def query(c: Conn, sql: String): Result[Array[Byte]] = ???\n\nclass RedisDB extends Database:\n  type Conn = redis.api.Connection     \u002F\u002F completely different type\n  def connect(): Conn = Redis.connect()\n  def query(c: Conn, sql: String): Result[Array[Byte]] = ???\n\n\u002F\u002F Path-dependent: db.Conn is specific to the db instance's type\ndef use(db: Database)(conn: db.Conn): Unit = ???  \u002F\u002F conn must match THIS db's Conn type\n\u002F\u002F This prevents passing a Postgres connection to a Redis database — type-safe at compile time!\n",[30,2242,2243,2248,2253,2257,2265,2276,2290,2333,2337,2350,2366,2386,2428,2432,2445,2461,2481,2523,2527,2532,2574],{"__ignoreMap":28},[33,2244,2245],{"class":35,"line":36},[33,2246,2247],{"class":39},"\u002F\u002F Abstract type member — declared in trait, implemented in subclass.\n",[33,2249,2250],{"class":35,"line":43},[33,2251,2252],{"class":39},"\u002F\u002F Unlike generics, the type is \"attached\" to the instance, not the call site.\n",[33,2254,2255],{"class":35,"line":49},[33,2256,172],{"emptyLinePlaceholder":171},[33,2258,2259,2261,2263],{"class":35,"line":110},[33,2260,458],{"class":52},[33,2262,1115],{"class":59},[33,2264,107],{"class":52},[33,2266,2267,2270,2273],{"class":35,"line":156},[33,2268,2269],{"class":52},"  type",[33,2271,2272],{"class":59}," Conn",[33,2274,2275],{"class":39},"                            \u002F\u002F abstract type — each DB impl defines its own connection type\n",[33,2277,2278,2280,2283,2285,2287],{"class":35,"line":168},[33,2279,247],{"class":52},[33,2281,2282],{"class":59}," connect",[33,2284,299],{"class":63},[33,2286,74],{"class":52},[33,2288,2289],{"class":59}," Conn\n",[33,2291,2292,2294,2297,2299,2302,2304,2307,2309,2312,2314,2316,2318,2320,2322,2324,2326,2328,2330],{"class":35,"line":175},[33,2293,247],{"class":52},[33,2295,2296],{"class":59}," query",[33,2298,64],{"class":63},[33,2300,2301],{"class":70},"c",[33,2303,98],{"class":63},[33,2305,2306],{"class":59},"Conn",[33,2308,80],{"class":63},[33,2310,2311],{"class":70},"sql",[33,2313,98],{"class":63},[33,2315,480],{"class":59},[33,2317,104],{"class":63},[33,2319,74],{"class":52},[33,2321,1350],{"class":59},[33,2323,1033],{"class":63},[33,2325,1901],{"class":59},[33,2327,1033],{"class":63},[33,2329,1906],{"class":59},[33,2331,2332],{"class":63},"]]\n",[33,2334,2335],{"class":35,"line":181},[33,2336,172],{"emptyLinePlaceholder":171},[33,2338,2339,2341,2344,2346,2348],{"class":35,"line":210},[33,2340,683],{"class":52},[33,2342,2343],{"class":59}," PostgresDB",[33,2345,517],{"class":52},[33,2347,1115],{"class":59},[33,2349,107],{"class":52},[33,2351,2352,2354,2356,2358,2361,2363],{"class":35,"line":233},[33,2353,2269],{"class":52},[33,2355,2272],{"class":59},[33,2357,198],{"class":52},[33,2359,2360],{"class":63}," java.sql.",[33,2362,1977],{"class":59},[33,2364,2365],{"class":39},"      \u002F\u002F concrete: Conn is a JDBC Connection\n",[33,2367,2368,2370,2372,2374,2376,2378,2380,2383],{"class":35,"line":238},[33,2369,247],{"class":52},[33,2371,2282],{"class":59},[33,2373,299],{"class":63},[33,2375,74],{"class":52},[33,2377,2272],{"class":59},[33,2379,198],{"class":52},[33,2381,2382],{"class":59}," DriverManager",[33,2384,2385],{"class":63},".getConnection(url)\n",[33,2387,2388,2390,2392,2394,2396,2398,2400,2402,2404,2406,2408,2410,2412,2414,2416,2418,2420,2422,2424,2426],{"class":35,"line":244},[33,2389,247],{"class":52},[33,2391,2296],{"class":59},[33,2393,64],{"class":63},[33,2395,2301],{"class":70},[33,2397,98],{"class":63},[33,2399,2306],{"class":59},[33,2401,80],{"class":63},[33,2403,2311],{"class":70},[33,2405,98],{"class":63},[33,2407,480],{"class":59},[33,2409,104],{"class":63},[33,2411,74],{"class":52},[33,2413,1350],{"class":59},[33,2415,1033],{"class":63},[33,2417,1901],{"class":59},[33,2419,1033],{"class":63},[33,2421,1906],{"class":59},[33,2423,1909],{"class":63},[33,2425,1234],{"class":52},[33,2427,1267],{"class":52},[33,2429,2430],{"class":35,"line":280},[33,2431,172],{"emptyLinePlaceholder":171},[33,2433,2434,2436,2439,2441,2443],{"class":35,"line":285},[33,2435,683],{"class":52},[33,2437,2438],{"class":59}," RedisDB",[33,2440,517],{"class":52},[33,2442,1115],{"class":59},[33,2444,107],{"class":52},[33,2446,2447,2449,2451,2453,2456,2458],{"class":35,"line":291},[33,2448,2269],{"class":52},[33,2450,2272],{"class":59},[33,2452,198],{"class":52},[33,2454,2455],{"class":63}," redis.api.",[33,2457,1977],{"class":59},[33,2459,2460],{"class":39},"     \u002F\u002F completely different type\n",[33,2462,2463,2465,2467,2469,2471,2473,2475,2478],{"class":35,"line":315},[33,2464,247],{"class":52},[33,2466,2282],{"class":59},[33,2468,299],{"class":63},[33,2470,74],{"class":52},[33,2472,2272],{"class":59},[33,2474,198],{"class":52},[33,2476,2477],{"class":59}," Redis",[33,2479,2480],{"class":63},".connect()\n",[33,2482,2483,2485,2487,2489,2491,2493,2495,2497,2499,2501,2503,2505,2507,2509,2511,2513,2515,2517,2519,2521],{"class":35,"line":327},[33,2484,247],{"class":52},[33,2486,2296],{"class":59},[33,2488,64],{"class":63},[33,2490,2301],{"class":70},[33,2492,98],{"class":63},[33,2494,2306],{"class":59},[33,2496,80],{"class":63},[33,2498,2311],{"class":70},[33,2500,98],{"class":63},[33,2502,480],{"class":59},[33,2504,104],{"class":63},[33,2506,74],{"class":52},[33,2508,1350],{"class":59},[33,2510,1033],{"class":63},[33,2512,1901],{"class":59},[33,2514,1033],{"class":63},[33,2516,1906],{"class":59},[33,2518,1909],{"class":63},[33,2520,1234],{"class":52},[33,2522,1267],{"class":52},[33,2524,2525],{"class":35,"line":333},[33,2526,172],{"emptyLinePlaceholder":171},[33,2528,2529],{"class":35,"line":338},[33,2530,2531],{"class":39},"\u002F\u002F Path-dependent: db.Conn is specific to the db instance's type\n",[33,2533,2534,2537,2540,2542,2545,2547,2550,2553,2556,2559,2561,2563,2565,2567,2569,2571],{"class":35,"line":344},[33,2535,2536],{"class":52},"def",[33,2538,2539],{"class":59}," use",[33,2541,64],{"class":63},[33,2543,2544],{"class":70},"db",[33,2546,98],{"class":63},[33,2548,2549],{"class":59},"Database",[33,2551,2552],{"class":63},")(",[33,2554,2555],{"class":70},"conn",[33,2557,2558],{"class":63},": db.",[33,2560,2306],{"class":59},[33,2562,104],{"class":63},[33,2564,74],{"class":52},[33,2566,304],{"class":59},[33,2568,198],{"class":52},[33,2570,1237],{"class":52},[33,2572,2573],{"class":39},"  \u002F\u002F conn must match THIS db's Conn type\n",[33,2575,2576],{"class":35,"line":384},[33,2577,2578],{"class":39},"\u002F\u002F This prevents passing a Postgres connection to a Redis database — type-safe at compile time!\n",[15,2580,2582],{"id":2581},"tips-tricks","💡 Tips & Tricks",[2584,2585,2586,2593,2594,2596],"p",{},[2587,2588,2589,2592],"strong",{},[30,2590,2591],{},"final class"," by default",": Mark classes ",[30,2595,53],{}," unless you intend them to be subclassed. The JIT can inline through final methods — measurable perf win.",[2584,2598,2599,2605,2606,2609,2610,2612],{},[2587,2600,2601,2604],{},[30,2602,2603],{},"enum"," for ADTs in Scala 3",": Replace ",[30,2607,2608],{},"sealed trait + case class\u002Fobject"," with ",[30,2611,2603],{}," for simpler syntax.",[20,2614,2615],{"language":22},[24,2616,2618],{"className":26,"code":2617,"language":22,"meta":28,"style":28},"enum Tree[+T]:\n  case Leaf(value: T)\n  case Node(left: Tree[T], right: Tree[T])\n\u002F\u002F Auto-generates: apply, unapply, toString, equals, hashCode, ordinal, values\n\u002F\u002F All variants are in the same file → exhaustiveness checking works\n",[30,2619,2620,2637,2654,2692,2697],{"__ignoreMap":28},[33,2621,2622,2624,2627,2629,2631,2633,2635],{"class":35,"line":36},[33,2623,2603],{"class":52},[33,2625,2626],{"class":59}," Tree",[33,2628,1033],{"class":63},[33,2630,1355],{"class":52},[33,2632,1188],{"class":59},[33,2634,1360],{"class":63},[33,2636,107],{"class":52},[33,2638,2639,2641,2644,2646,2648,2650,2652],{"class":35,"line":43},[33,2640,1465],{"class":52},[33,2642,2643],{"class":59}," Leaf",[33,2645,64],{"class":63},[33,2647,1478],{"class":70},[33,2649,98],{"class":63},[33,2651,1188],{"class":59},[33,2653,165],{"class":63},[33,2655,2656,2658,2661,2663,2666,2668,2671,2673,2675,2678,2681,2683,2685,2687,2689],{"class":35,"line":49},[33,2657,1465],{"class":52},[33,2659,2660],{"class":59}," Node",[33,2662,64],{"class":63},[33,2664,2665],{"class":70},"left",[33,2667,98],{"class":63},[33,2669,2670],{"class":59},"Tree",[33,2672,1033],{"class":63},[33,2674,1188],{"class":59},[33,2676,2677],{"class":63},"], ",[33,2679,2680],{"class":70},"right",[33,2682,98],{"class":63},[33,2684,2670],{"class":59},[33,2686,1033],{"class":63},[33,2688,1188],{"class":59},[33,2690,2691],{"class":63},"])\n",[33,2693,2694],{"class":35,"line":110},[33,2695,2696],{"class":39},"\u002F\u002F Auto-generates: apply, unapply, toString, equals, hashCode, ordinal, values\n",[33,2698,2699],{"class":35,"line":156},[33,2700,2701],{"class":39},"\u002F\u002F All variants are in the same file → exhaustiveness checking works\n",[2584,2703,2704,2714,2715,2718],{},[2587,2705,2706,2709,2710,2713],{},[30,2707,2708],{},"selectDynamic"," \u002F ",[30,2711,2712],{},"applyDynamic"," for structural DSLs",": Scala 3's ",[30,2716,2717],{},"Selectable"," trait enables dynamic member access for building fluid DSLs.",[15,2720,2722],{"id":2721},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2584,2724,2725,2731,2732,2734,2735,2737,2738,274,2741,2744],{},[2587,2726,2727,2728,2730],{},"Trait ",[30,2729,67],{}," initialization order",": Traits initialize in linearization order. A ",[30,2733,67],{}," in a trait that depends on an abstract ",[30,2736,67],{}," from a later trait will see ",[30,2739,2740],{},"0",[30,2742,2743],{},"null",".",[20,2746,2747],{"language":22},[24,2748,2750],{"className":26,"code":2749,"language":22,"meta":28,"style":28},"trait Metric:\n  val name: String                        \u002F\u002F abstract\n  val fullName = s\"metric.$name\"          \u002F\u002F evaluated during trait init — name may be null!\n\nclass CpuMetric extends Metric:\n  val name = \"cpu\"                        \u002F\u002F initialized AFTER Metric's fullName → fullName = \"metric.null\"\n\n\u002F\u002F Fix: use lazy val, def, or pre-initialized fields:\nclass CpuMetricSafe extends:\n  val name = \"cpu\"                         \u002F\u002F pre-init: name set BEFORE Metric trait initializes\nwith Metric\n",[30,2751,2752,2761,2775,2797,2801,2814,2828,2832,2837,2847,2860],{"__ignoreMap":28},[33,2753,2754,2756,2759],{"class":35,"line":36},[33,2755,458],{"class":52},[33,2757,2758],{"class":59}," Metric",[33,2760,107],{"class":52},[33,2762,2763,2765,2768,2770,2772],{"class":35,"line":43},[33,2764,759],{"class":52},[33,2766,2767],{"class":70}," name",[33,2769,74],{"class":52},[33,2771,77],{"class":59},[33,2773,2774],{"class":39},"                        \u002F\u002F abstract\n",[33,2776,2777,2779,2782,2784,2786,2789,2792,2794],{"class":35,"line":49},[33,2778,759],{"class":52},[33,2780,2781],{"class":70}," fullName",[33,2783,198],{"class":52},[33,2785,362],{"class":52},[33,2787,2788],{"class":140},"\"metric.",[33,2790,2791],{"class":63},"$name",[33,2793,147],{"class":140},[33,2795,2796],{"class":39},"          \u002F\u002F evaluated during trait init — name may be null!\n",[33,2798,2799],{"class":35,"line":110},[33,2800,172],{"emptyLinePlaceholder":171},[33,2802,2803,2805,2808,2810,2812],{"class":35,"line":156},[33,2804,683],{"class":52},[33,2806,2807],{"class":59}," CpuMetric",[33,2809,517],{"class":52},[33,2811,2758],{"class":59},[33,2813,107],{"class":52},[33,2815,2816,2818,2820,2822,2825],{"class":35,"line":168},[33,2817,759],{"class":52},[33,2819,2767],{"class":70},[33,2821,198],{"class":52},[33,2823,2824],{"class":140}," \"cpu\"",[33,2826,2827],{"class":39},"                        \u002F\u002F initialized AFTER Metric's fullName → fullName = \"metric.null\"\n",[33,2829,2830],{"class":35,"line":175},[33,2831,172],{"emptyLinePlaceholder":171},[33,2833,2834],{"class":35,"line":181},[33,2835,2836],{"class":39},"\u002F\u002F Fix: use lazy val, def, or pre-initialized fields:\n",[33,2838,2839,2841,2844],{"class":35,"line":210},[33,2840,683],{"class":52},[33,2842,2843],{"class":59}," CpuMetricSafe",[33,2845,2846],{"class":52}," extends:\n",[33,2848,2849,2851,2853,2855,2857],{"class":35,"line":233},[33,2850,759],{"class":52},[33,2852,2767],{"class":70},[33,2854,198],{"class":52},[33,2856,2824],{"class":140},[33,2858,2859],{"class":39},"                         \u002F\u002F pre-init: name set BEFORE Metric trait initializes\n",[33,2861,2862,2865],{"class":35,"line":238},[33,2863,2864],{"class":52},"with",[33,2866,2867],{"class":59}," Metric\n",[2584,2869,2870,2876,2877,2879,2880,2883,2884,2887],{},[2587,2871,2872,2875],{},[30,2873,2874],{},"equals"," on mutable fields",": If ",[30,2878,2874],{}," depends on a ",[30,2881,2882],{},"var",", storing the object in a ",[30,2885,2886],{},"HashSet"," then mutating the field makes it unfindable — the hash bucket no longer matches.",[2584,2889,2890,2895,2896,2898],{},[2587,2891,2892,2894],{},[30,2893,1456],{}," is lazy",": The singleton is initialized on first access, not at class load. Side effects in ",[30,2897,1456],{}," bodies run at an unpredictable time.",[15,2900,2902],{"id":2901},"quick-quiz","🧠 Quick Quiz",[2584,2904,2905,2906,2909,2910,2913,2914,2917],{},"What's the linearization of ",[30,2907,2908],{},"class X extends A with B with C"," where ",[30,2911,2912],{},"B extends A"," and ",[30,2915,2916],{},"C extends A","?",[2919,2920,2921,2925,2928,2968,2974],"details",{},[2922,2923,2924],"summary",{},"Answer",[2584,2926,2927],{},"Linearization is computed right-to-left, depth-first, removing duplicates (keeping the rightmost occurrence):",[2929,2930,2931,2938,2951,2961],"ol",{},[2932,2933,2934,2935],"li",{},"Start with ",[30,2936,2937],{},"X",[2932,2939,2940,2943,2944,2946,2947,2950],{},[30,2941,2942],{},"C"," → ",[30,2945,2942],{}," linearizes to ",[30,2948,2949],{},"C, A"," (C extends A)",[2932,2952,2953,2943,2956,2946,2958],{},[30,2954,2955],{},"B",[30,2957,2955],{},[30,2959,2960],{},"B, A",[2932,2962,2963,2943,2966],{},[30,2964,2965],{},"A",[30,2967,2965],{},[2584,2969,2970,2971],{},"Assembling right-to-left: ",[30,2972,2973],{},"X, C, B, A, AnyRef, Any",[2584,2975,2976,2977,2980,2981,2983,2984,80,2986,2980,2988,2983,2990,2992,2993,2995,2996,2999,3000,3003,3004,3006],{},"So ",[30,2978,2979],{},"super"," in ",[30,2982,2942],{}," calls ",[30,2985,2955],{},[30,2987,2979],{},[30,2989,2955],{},[30,2991,2965],{},". The rightmost trait (",[30,2994,2942],{},") gets the \"last word\" in override chains. This is why trait ordering matters — ",[30,2997,2998],{},"with B with C"," vs ",[30,3001,3002],{},"with C with B"," produce different linearizations and different ",[30,3005,2979],{}," call chains.",[3008,3009,3010],"style",{},"html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}html pre.shiki code .sCrzJ, html code.shiki .sCrzJ{--shiki-default:#E36209;--shiki-github-dark:#FFAB70}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}",{"title":28,"searchDepth":43,"depth":43,"links":3012},[3013,3014,3015,3016,3018,3020,3021,3022,3023,3024],{"id":17,"depth":43,"text":18},{"id":426,"depth":43,"text":427},{"id":895,"depth":43,"text":896},{"id":1149,"depth":43,"text":3017},"open Classes & Sealed Hierarchies — Scala 3",{"id":1547,"depth":43,"text":3019},"Universal Equality & canEqual",{"id":1814,"depth":43,"text":1815},{"id":2234,"depth":43,"text":2235},{"id":2581,"depth":43,"text":2582},{"id":2721,"depth":43,"text":2722},{"id":2901,"depth":43,"text":2902},"[object Object]","md",{},"\u002Fscala\u002F06-classes-and-objects",{"title":5,"description":3030},{"Deep-dive into Scala's OOP":3031},"trait linearization, super calls, self types, open classes, universal equality, and the JVM representation of traits and objects.","scala\u002F06-classes-and-objects","E4veb-RNiudG_kJLRZRqp3KTxNsUO4BwCdfBxKKfxUs",1789924653989]