[{"data":1,"prerenderedAt":3102},["ShallowReactive",2],{"page-\u002Fscala\u002F11-java-interoperability":3},{"id":4,"title":5,"body":6,"description":28,"extension":3093,"meta":3094,"navigation":67,"path":3095,"seo":3096,"stem":3100,"__hash__":3101},"content\u002Fscala\u002F11-java-interoperability.md","[object Object]",{"type":7,"value":8,"toc":3077},"minimark",[9,14,19,444,455,742,746,1125,1129,1450,1457,1702,1706,3073],[10,11,13],"h1",{"id":12},"_11-java-interoperability-collection-conversion-null-safety-bytecode-boundaries","11 — Java Interoperability: Collection Conversion, Null Safety & Bytecode Boundaries",[15,16,18],"h2",{"id":17},"collection-conversion-views-vs-copies","Collection Conversion — Views vs Copies",[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","import scala.jdk.CollectionConverters.*\n\n\u002F\u002F asScala \u002F asJava create VIEWS, not copies — mutations propagate both directions!\nval javaList = new java.util.ArrayList[Int]()\njavaList.add(1); javaList.add(2)\n\nval scalaView = javaList.asScala             \u002F\u002F mutable.Buffer[Int] — a VIEW over javaList\nscalaView(0) = 99                            \u002F\u002F MUTATES the original javaList!\nprintln(javaList.get(0))                     \u002F\u002F → 99 — the Java list is modified\n\njavaList.add(3)\nprintln(scalaView.toList)                    \u002F\u002F → List(99, 2, 3) — view sees Java's additions\n\n\u002F\u002F ❌ ANTI-PATTERN: assuming asScala gives an immutable snapshot\ndef process(items: java.util.List[Int]): List[Int] =\n  items.asScala.map(_ * 2).toList            \u002F\u002F if items is mutated during map, results are wrong\n  \u002F\u002F The view reflects live mutations — race condition if another thread modifies items\n\n\u002F\u002F ✅ CORRECT: materialize to immutable copy if you need isolation\ndef processSafe(items: java.util.List[Int]): List[Int] =\n  val snapshot = items.asScala.toVector      \u002F\u002F copy — now independent of original\n  snapshot.map(_ * 2)                        \u002F\u002F safe even if items is mutated concurrently\n\n\u002F\u002F Direction-specific conversion:\n\u002F\u002F   java.util.List    → asScala → scala.collection.mutable.Buffer  (view, mutable)\n\u002F\u002F   scala List\u002FVector → asJava  → java.util.List                   (view, immutable backing)\n\u002F\u002F   scala mutable.Map → asJava  → java.util.Map                    (view, mutable)\nval sList = List(1, 2, 3)\nval jView = sList.asJava                     \u002F\u002F java.util.List — backed by the immutable List\n\u002F\u002F jView.add(4)  \u002F\u002F UnsupportedOperationException — can't modify immutable backing!\n","",[30,31,32,62,69,76,107,126,131,147,168,182,187,197,206,211,217,261,279,285,290,296,330,347,363,368,374,380,386,392,419,435],"code",{"__ignoreMap":28},[33,34,37,41,45,49,52,54,57,59],"span",{"class":35,"line":36},"line",1,[33,38,40],{"class":39},"svdQ7","import",[33,42,44],{"class":43},"sIsaT"," scala",[33,46,48],{"class":47},"ssxIu",".",[33,50,51],{"class":43},"jdk",[33,53,48],{"class":47},[33,55,56],{"class":43},"CollectionConverters",[33,58,48],{"class":47},[33,60,61],{"class":43},"*\n",[33,63,65],{"class":35,"line":64},2,[33,66,68],{"emptyLinePlaceholder":67},true,"\n",[33,70,72],{"class":35,"line":71},3,[33,73,75],{"class":74},"sdCPZ","\u002F\u002F asScala \u002F asJava create VIEWS, not copies — mutations propagate both directions!\n",[33,77,79,82,86,89,92,95,98,101,104],{"class":35,"line":78},4,[33,80,81],{"class":39},"val",[33,83,85],{"class":84},"sCrzJ"," javaList",[33,87,88],{"class":39}," =",[33,90,91],{"class":39}," new",[33,93,94],{"class":47}," java.util.",[33,96,97],{"class":43},"ArrayList",[33,99,100],{"class":47},"[",[33,102,103],{"class":43},"Int",[33,105,106],{"class":47},"]()\n",[33,108,110,113,117,120,123],{"class":35,"line":109},5,[33,111,112],{"class":47},"javaList.add(",[33,114,116],{"class":115},"snvgF","1",[33,118,119],{"class":47},"); javaList.add(",[33,121,122],{"class":115},"2",[33,124,125],{"class":47},")\n",[33,127,129],{"class":35,"line":128},6,[33,130,68],{"emptyLinePlaceholder":67},[33,132,134,136,139,141,144],{"class":35,"line":133},7,[33,135,81],{"class":39},[33,137,138],{"class":84}," scalaView",[33,140,88],{"class":39},[33,142,143],{"class":47}," javaList.asScala             ",[33,145,146],{"class":74},"\u002F\u002F mutable.Buffer[Int] — a VIEW over javaList\n",[33,148,150,153,156,159,162,165],{"class":35,"line":149},8,[33,151,152],{"class":47},"scalaView(",[33,154,155],{"class":115},"0",[33,157,158],{"class":47},") ",[33,160,161],{"class":39},"=",[33,163,164],{"class":115}," 99",[33,166,167],{"class":74},"                            \u002F\u002F MUTATES the original javaList!\n",[33,169,171,174,176,179],{"class":35,"line":170},9,[33,172,173],{"class":47},"println(javaList.get(",[33,175,155],{"class":115},[33,177,178],{"class":47},"))                     ",[33,180,181],{"class":74},"\u002F\u002F → 99 — the Java list is modified\n",[33,183,185],{"class":35,"line":184},10,[33,186,68],{"emptyLinePlaceholder":67},[33,188,190,192,195],{"class":35,"line":189},11,[33,191,112],{"class":47},[33,193,194],{"class":115},"3",[33,196,125],{"class":47},[33,198,200,203],{"class":35,"line":199},12,[33,201,202],{"class":47},"println(scalaView.toList)                    ",[33,204,205],{"class":74},"\u002F\u002F → List(99, 2, 3) — view sees Java's additions\n",[33,207,209],{"class":35,"line":208},13,[33,210,68],{"emptyLinePlaceholder":67},[33,212,214],{"class":35,"line":213},14,[33,215,216],{"class":74},"\u002F\u002F ❌ ANTI-PATTERN: assuming asScala gives an immutable snapshot\n",[33,218,220,223,226,229,232,235,238,240,242,245,248,251,253,255,258],{"class":35,"line":219},15,[33,221,222],{"class":39},"def",[33,224,225],{"class":43}," process",[33,227,228],{"class":47},"(",[33,230,231],{"class":84},"items",[33,233,234],{"class":47},": java.util.",[33,236,237],{"class":43},"List",[33,239,100],{"class":47},[33,241,103],{"class":43},[33,243,244],{"class":47},"])",[33,246,247],{"class":39},":",[33,249,250],{"class":43}," List",[33,252,100],{"class":47},[33,254,103],{"class":43},[33,256,257],{"class":47},"] ",[33,259,260],{"class":39},"=\n",[33,262,264,267,270,273,276],{"class":35,"line":263},16,[33,265,266],{"class":47},"  items.asScala.map(_ ",[33,268,269],{"class":39},"*",[33,271,272],{"class":115}," 2",[33,274,275],{"class":47},").toList            ",[33,277,278],{"class":74},"\u002F\u002F if items is mutated during map, results are wrong\n",[33,280,282],{"class":35,"line":281},17,[33,283,284],{"class":74},"  \u002F\u002F The view reflects live mutations — race condition if another thread modifies items\n",[33,286,288],{"class":35,"line":287},18,[33,289,68],{"emptyLinePlaceholder":67},[33,291,293],{"class":35,"line":292},19,[33,294,295],{"class":74},"\u002F\u002F ✅ CORRECT: materialize to immutable copy if you need isolation\n",[33,297,299,301,304,306,308,310,312,314,316,318,320,322,324,326,328],{"class":35,"line":298},20,[33,300,222],{"class":39},[33,302,303],{"class":43}," processSafe",[33,305,228],{"class":47},[33,307,231],{"class":84},[33,309,234],{"class":47},[33,311,237],{"class":43},[33,313,100],{"class":47},[33,315,103],{"class":43},[33,317,244],{"class":47},[33,319,247],{"class":39},[33,321,250],{"class":43},[33,323,100],{"class":47},[33,325,103],{"class":43},[33,327,257],{"class":47},[33,329,260],{"class":39},[33,331,333,336,339,341,344],{"class":35,"line":332},21,[33,334,335],{"class":39},"  val",[33,337,338],{"class":84}," snapshot",[33,340,88],{"class":39},[33,342,343],{"class":47}," items.asScala.toVector      ",[33,345,346],{"class":74},"\u002F\u002F copy — now independent of original\n",[33,348,350,353,355,357,360],{"class":35,"line":349},22,[33,351,352],{"class":47},"  snapshot.map(_ ",[33,354,269],{"class":39},[33,356,272],{"class":115},[33,358,359],{"class":47},")                        ",[33,361,362],{"class":74},"\u002F\u002F safe even if items is mutated concurrently\n",[33,364,366],{"class":35,"line":365},23,[33,367,68],{"emptyLinePlaceholder":67},[33,369,371],{"class":35,"line":370},24,[33,372,373],{"class":74},"\u002F\u002F Direction-specific conversion:\n",[33,375,377],{"class":35,"line":376},25,[33,378,379],{"class":74},"\u002F\u002F   java.util.List    → asScala → scala.collection.mutable.Buffer  (view, mutable)\n",[33,381,383],{"class":35,"line":382},26,[33,384,385],{"class":74},"\u002F\u002F   scala List\u002FVector → asJava  → java.util.List                   (view, immutable backing)\n",[33,387,389],{"class":35,"line":388},27,[33,390,391],{"class":74},"\u002F\u002F   scala mutable.Map → asJava  → java.util.Map                    (view, mutable)\n",[33,393,395,397,400,402,404,406,408,411,413,415,417],{"class":35,"line":394},28,[33,396,81],{"class":39},[33,398,399],{"class":84}," sList",[33,401,88],{"class":39},[33,403,250],{"class":43},[33,405,228],{"class":47},[33,407,116],{"class":115},[33,409,410],{"class":47},", ",[33,412,122],{"class":115},[33,414,410],{"class":47},[33,416,194],{"class":115},[33,418,125],{"class":47},[33,420,422,424,427,429,432],{"class":35,"line":421},29,[33,423,81],{"class":39},[33,425,426],{"class":84}," jView",[33,428,88],{"class":39},[33,430,431],{"class":47}," sList.asJava                     ",[33,433,434],{"class":74},"\u002F\u002F java.util.List — backed by the immutable List\n",[33,436,438,441],{"class":35,"line":437},30,[33,439,440],{"class":74},"\u002F\u002F jView.add(4)",[33,442,443],{"class":74},"  \u002F\u002F UnsupportedOperationException — can't modify immutable backing!\n",[15,445,447,448,451,452],{"id":446},"null-handling-option-java-optional","Null Handling — ",[30,449,450],{},"Option"," ↔ Java ",[30,453,454],{},"Optional",[20,456,457],{"language":22},[24,458,460],{"className":26,"code":459,"language":22,"meta":28,"style":28},"import scala.jdk.OptionConverters.*\n\n\u002F\u002F Java methods return null — wrap with Option for null-safety\ndef findJavaUser(id: Long): String = userDao.findById(id)  \u002F\u002F may return null\n\nval safe: Option[String] = Option(findJavaUser(42))\n\u002F\u002F   null → None\n\u002F\u002F   \"Alice\" → Some(\"Alice\")\n\u002F\u002F Option(null) == None — the constructor handles null automatically\n\n\u002F\u002F Java Optional → Scala Option\nval javaOpt: java.util.Optional[String] = javaService.findName(42)\nval scalaOpt: Option[String] = javaOpt.toScala  \u002F\u002F Optional.empty → None, Optional.of → Some\n\n\u002F\u002F Scala Option → Java Optional\nval sOpt: Option[String] = Some(\"hello\")\nval jOpt: java.util.Optional[String] = sOpt.toJava  \u002F\u002F Some → Optional.of, None → Optional.empty\n\n\u002F\u002F ❌ ANTI-PATTERN: calling .get() on Java Optional (throws NoSuchElementException)\n\u002F\u002F ✅ CORRECT: convert to Scala Option and use functional combinators\njavaService.findName(42).toScala\n  .map(_.toUpperCase)\n  .getOrElse(\"UNKNOWN\")\n",[30,461,462,481,485,490,524,528,560,565,570,575,579,584,612,637,641,646,676,703,707,712,717,727,732],{"__ignoreMap":28},[33,463,464,466,468,470,472,474,477,479],{"class":35,"line":36},[33,465,40],{"class":39},[33,467,44],{"class":43},[33,469,48],{"class":47},[33,471,51],{"class":43},[33,473,48],{"class":47},[33,475,476],{"class":43},"OptionConverters",[33,478,48],{"class":47},[33,480,61],{"class":43},[33,482,483],{"class":35,"line":64},[33,484,68],{"emptyLinePlaceholder":67},[33,486,487],{"class":35,"line":71},[33,488,489],{"class":74},"\u002F\u002F Java methods return null — wrap with Option for null-safety\n",[33,491,492,494,497,499,502,505,508,511,513,516,518,521],{"class":35,"line":78},[33,493,222],{"class":39},[33,495,496],{"class":43}," findJavaUser",[33,498,228],{"class":47},[33,500,501],{"class":84},"id",[33,503,504],{"class":47},": ",[33,506,507],{"class":43},"Long",[33,509,510],{"class":47},")",[33,512,247],{"class":39},[33,514,515],{"class":43}," String",[33,517,88],{"class":39},[33,519,520],{"class":47}," userDao.findById(id)  ",[33,522,523],{"class":74},"\u002F\u002F may return null\n",[33,525,526],{"class":35,"line":109},[33,527,68],{"emptyLinePlaceholder":67},[33,529,530,532,535,537,540,542,545,547,549,551,554,557],{"class":35,"line":128},[33,531,81],{"class":39},[33,533,534],{"class":84}," safe",[33,536,247],{"class":39},[33,538,539],{"class":43}," Option",[33,541,100],{"class":47},[33,543,544],{"class":43},"String",[33,546,257],{"class":47},[33,548,161],{"class":39},[33,550,539],{"class":43},[33,552,553],{"class":47},"(findJavaUser(",[33,555,556],{"class":115},"42",[33,558,559],{"class":47},"))\n",[33,561,562],{"class":35,"line":133},[33,563,564],{"class":74},"\u002F\u002F   null → None\n",[33,566,567],{"class":35,"line":149},[33,568,569],{"class":74},"\u002F\u002F   \"Alice\" → Some(\"Alice\")\n",[33,571,572],{"class":35,"line":170},[33,573,574],{"class":74},"\u002F\u002F Option(null) == None — the constructor handles null automatically\n",[33,576,577],{"class":35,"line":184},[33,578,68],{"emptyLinePlaceholder":67},[33,580,581],{"class":35,"line":189},[33,582,583],{"class":74},"\u002F\u002F Java Optional → Scala Option\n",[33,585,586,588,591,593,595,597,599,601,603,605,608,610],{"class":35,"line":199},[33,587,81],{"class":39},[33,589,590],{"class":84}," javaOpt",[33,592,247],{"class":39},[33,594,94],{"class":47},[33,596,454],{"class":43},[33,598,100],{"class":47},[33,600,544],{"class":43},[33,602,257],{"class":47},[33,604,161],{"class":39},[33,606,607],{"class":47}," javaService.findName(",[33,609,556],{"class":115},[33,611,125],{"class":47},[33,613,614,616,619,621,623,625,627,629,631,634],{"class":35,"line":208},[33,615,81],{"class":39},[33,617,618],{"class":84}," scalaOpt",[33,620,247],{"class":39},[33,622,539],{"class":43},[33,624,100],{"class":47},[33,626,544],{"class":43},[33,628,257],{"class":47},[33,630,161],{"class":39},[33,632,633],{"class":47}," javaOpt.toScala  ",[33,635,636],{"class":74},"\u002F\u002F Optional.empty → None, Optional.of → Some\n",[33,638,639],{"class":35,"line":213},[33,640,68],{"emptyLinePlaceholder":67},[33,642,643],{"class":35,"line":219},[33,644,645],{"class":74},"\u002F\u002F Scala Option → Java Optional\n",[33,647,648,650,653,655,657,659,661,663,665,668,670,674],{"class":35,"line":263},[33,649,81],{"class":39},[33,651,652],{"class":84}," sOpt",[33,654,247],{"class":39},[33,656,539],{"class":43},[33,658,100],{"class":47},[33,660,544],{"class":43},[33,662,257],{"class":47},[33,664,161],{"class":39},[33,666,667],{"class":43}," Some",[33,669,228],{"class":47},[33,671,673],{"class":672},"sJ6F3","\"hello\"",[33,675,125],{"class":47},[33,677,678,680,683,685,687,689,691,693,695,697,700],{"class":35,"line":281},[33,679,81],{"class":39},[33,681,682],{"class":84}," jOpt",[33,684,247],{"class":39},[33,686,94],{"class":47},[33,688,454],{"class":43},[33,690,100],{"class":47},[33,692,544],{"class":43},[33,694,257],{"class":47},[33,696,161],{"class":39},[33,698,699],{"class":47}," sOpt.toJava  ",[33,701,702],{"class":74},"\u002F\u002F Some → Optional.of, None → Optional.empty\n",[33,704,705],{"class":35,"line":287},[33,706,68],{"emptyLinePlaceholder":67},[33,708,709],{"class":35,"line":292},[33,710,711],{"class":74},"\u002F\u002F ❌ ANTI-PATTERN: calling .get() on Java Optional (throws NoSuchElementException)\n",[33,713,714],{"class":35,"line":298},[33,715,716],{"class":74},"\u002F\u002F ✅ CORRECT: convert to Scala Option and use functional combinators\n",[33,718,719,722,724],{"class":35,"line":332},[33,720,721],{"class":47},"javaService.findName(",[33,723,556],{"class":115},[33,725,726],{"class":47},").toScala\n",[33,728,729],{"class":35,"line":349},[33,730,731],{"class":47},"  .map(_.toUpperCase)\n",[33,733,734,737,740],{"class":35,"line":365},[33,735,736],{"class":47},"  .getOrElse(",[33,738,739],{"class":672},"\"UNKNOWN\"",[33,741,125],{"class":47},[15,743,745],{"id":744},"checked-exceptions-scala-doesnt-have-them","Checked Exceptions — Scala Doesn't Have Them",[20,747,748],{"language":22},[24,749,751],{"className":26,"code":750,"language":22,"meta":28,"style":28},"import java.io.{IOException, FileReader}\n\n\u002F\u002F Java: methods must declare `throws IOException` — compiler-enforced.\n\u002F\u002F Scala: NO checked exceptions. You can throw any exception without declaring it.\n\n\u002F\u002F ❌ DANGEROUS: calling Java code that throws checked exceptions without handling\ndef readConfig(path: String): String =\n  val reader = new FileReader(path)         \u002F\u002F throws IOException — but Scala doesn't force handling\n  reader.read().toString                    \u002F\u002F if file missing → IOException at runtime, uncaught\n\n\u002F\u002F ✅ CORRECT: wrap in Try for compositional error handling\nimport scala.util.Try\ndef readConfigSafe(path: String): Try[String] = Try {\n  val reader = new FileReader(path)\n  try reader.read().toString finally reader.close()\n}\n\nreadConfigSafe(\"missing.conf\") match\n  case Success(content) => parseConfig(content)\n  case Failure(e: IOException) => println(s\"File not found: ${e.getMessage}\")\n  case Failure(e) => println(s\"Unexpected: $e\")\n\n\u002F\u002F ✅ CORRECT: Using.resource for automatic cleanup (Scala 2.13+ \u002F 3.x)\ndef readConfigUsing(path: String): Try[String] = Try {\n  scala.util.Using.resource(new FileReader(path)) { reader =>\n    reader.read().toString                  \u002F\u002F reader.close() called automatically\n  }\n}\n",[30,752,753,779,783,788,793,797,802,827,847,855,859,864,880,915,930,944,948,952,965,982,1019,1044,1048,1053,1086,1108,1116,1121],{"__ignoreMap":28},[33,754,755,757,760,762,765,768,771,773,776],{"class":35,"line":36},[33,756,40],{"class":39},[33,758,759],{"class":43}," java",[33,761,48],{"class":47},[33,763,764],{"class":43},"io",[33,766,767],{"class":47},".{",[33,769,770],{"class":43},"IOException",[33,772,410],{"class":47},[33,774,775],{"class":43},"FileReader",[33,777,778],{"class":47},"}\n",[33,780,781],{"class":35,"line":64},[33,782,68],{"emptyLinePlaceholder":67},[33,784,785],{"class":35,"line":71},[33,786,787],{"class":74},"\u002F\u002F Java: methods must declare `throws IOException` — compiler-enforced.\n",[33,789,790],{"class":35,"line":78},[33,791,792],{"class":74},"\u002F\u002F Scala: NO checked exceptions. You can throw any exception without declaring it.\n",[33,794,795],{"class":35,"line":109},[33,796,68],{"emptyLinePlaceholder":67},[33,798,799],{"class":35,"line":128},[33,800,801],{"class":74},"\u002F\u002F ❌ DANGEROUS: calling Java code that throws checked exceptions without handling\n",[33,803,804,806,809,811,814,816,818,820,822,824],{"class":35,"line":133},[33,805,222],{"class":39},[33,807,808],{"class":43}," readConfig",[33,810,228],{"class":47},[33,812,813],{"class":84},"path",[33,815,504],{"class":47},[33,817,544],{"class":43},[33,819,510],{"class":47},[33,821,247],{"class":39},[33,823,515],{"class":43},[33,825,826],{"class":39}," =\n",[33,828,829,831,834,836,838,841,844],{"class":35,"line":149},[33,830,335],{"class":39},[33,832,833],{"class":84}," reader",[33,835,88],{"class":39},[33,837,91],{"class":39},[33,839,840],{"class":43}," FileReader",[33,842,843],{"class":47},"(path)         ",[33,845,846],{"class":74},"\u002F\u002F throws IOException — but Scala doesn't force handling\n",[33,848,849,852],{"class":35,"line":170},[33,850,851],{"class":47},"  reader.read().toString                    ",[33,853,854],{"class":74},"\u002F\u002F if file missing → IOException at runtime, uncaught\n",[33,856,857],{"class":35,"line":184},[33,858,68],{"emptyLinePlaceholder":67},[33,860,861],{"class":35,"line":189},[33,862,863],{"class":74},"\u002F\u002F ✅ CORRECT: wrap in Try for compositional error handling\n",[33,865,866,868,870,872,875,877],{"class":35,"line":199},[33,867,40],{"class":39},[33,869,44],{"class":43},[33,871,48],{"class":47},[33,873,874],{"class":43},"util",[33,876,48],{"class":47},[33,878,879],{"class":43},"Try\n",[33,881,882,884,887,889,891,893,895,897,899,902,904,906,908,910,912],{"class":35,"line":208},[33,883,222],{"class":39},[33,885,886],{"class":43}," readConfigSafe",[33,888,228],{"class":47},[33,890,813],{"class":84},[33,892,504],{"class":47},[33,894,544],{"class":43},[33,896,510],{"class":47},[33,898,247],{"class":39},[33,900,901],{"class":43}," Try",[33,903,100],{"class":47},[33,905,544],{"class":43},[33,907,257],{"class":47},[33,909,161],{"class":39},[33,911,901],{"class":43},[33,913,914],{"class":47}," {\n",[33,916,917,919,921,923,925,927],{"class":35,"line":213},[33,918,335],{"class":39},[33,920,833],{"class":84},[33,922,88],{"class":39},[33,924,91],{"class":39},[33,926,840],{"class":43},[33,928,929],{"class":47},"(path)\n",[33,931,932,935,938,941],{"class":35,"line":219},[33,933,934],{"class":39},"  try",[33,936,937],{"class":47}," reader.read().toString ",[33,939,940],{"class":39},"finally",[33,942,943],{"class":47}," reader.close()\n",[33,945,946],{"class":35,"line":263},[33,947,778],{"class":47},[33,949,950],{"class":35,"line":281},[33,951,68],{"emptyLinePlaceholder":67},[33,953,954,957,960,962],{"class":35,"line":287},[33,955,956],{"class":47},"readConfigSafe(",[33,958,959],{"class":672},"\"missing.conf\"",[33,961,158],{"class":47},[33,963,964],{"class":39},"match\n",[33,966,967,970,973,976,979],{"class":35,"line":292},[33,968,969],{"class":39},"  case",[33,971,972],{"class":43}," Success",[33,974,975],{"class":47},"(content) ",[33,977,978],{"class":39},"=>",[33,980,981],{"class":47}," parseConfig(content)\n",[33,983,984,986,989,991,994,996,998,1000,1002,1005,1008,1011,1014,1017],{"class":35,"line":298},[33,985,969],{"class":39},[33,987,988],{"class":43}," Failure",[33,990,228],{"class":47},[33,992,993],{"class":84},"e",[33,995,504],{"class":47},[33,997,770],{"class":43},[33,999,158],{"class":47},[33,1001,978],{"class":39},[33,1003,1004],{"class":47}," println(",[33,1006,1007],{"class":39},"s",[33,1009,1010],{"class":672},"\"File not found: ",[33,1012,1013],{"class":47},"${e.getMessage}",[33,1015,1016],{"class":672},"\"",[33,1018,125],{"class":47},[33,1020,1021,1023,1025,1028,1030,1032,1034,1037,1040,1042],{"class":35,"line":332},[33,1022,969],{"class":39},[33,1024,988],{"class":43},[33,1026,1027],{"class":47},"(e) ",[33,1029,978],{"class":39},[33,1031,1004],{"class":47},[33,1033,1007],{"class":39},[33,1035,1036],{"class":672},"\"Unexpected: ",[33,1038,1039],{"class":47},"$e",[33,1041,1016],{"class":672},[33,1043,125],{"class":47},[33,1045,1046],{"class":35,"line":349},[33,1047,68],{"emptyLinePlaceholder":67},[33,1049,1050],{"class":35,"line":365},[33,1051,1052],{"class":74},"\u002F\u002F ✅ CORRECT: Using.resource for automatic cleanup (Scala 2.13+ \u002F 3.x)\n",[33,1054,1055,1057,1060,1062,1064,1066,1068,1070,1072,1074,1076,1078,1080,1082,1084],{"class":35,"line":370},[33,1056,222],{"class":39},[33,1058,1059],{"class":43}," readConfigUsing",[33,1061,228],{"class":47},[33,1063,813],{"class":84},[33,1065,504],{"class":47},[33,1067,544],{"class":43},[33,1069,510],{"class":47},[33,1071,247],{"class":39},[33,1073,901],{"class":43},[33,1075,100],{"class":47},[33,1077,544],{"class":43},[33,1079,257],{"class":47},[33,1081,161],{"class":39},[33,1083,901],{"class":43},[33,1085,914],{"class":47},[33,1087,1088,1091,1094,1097,1100,1102,1105],{"class":35,"line":376},[33,1089,1090],{"class":47},"  scala.util.",[33,1092,1093],{"class":43},"Using",[33,1095,1096],{"class":47},".resource(",[33,1098,1099],{"class":39},"new",[33,1101,840],{"class":43},[33,1103,1104],{"class":47},"(path)) { reader ",[33,1106,1107],{"class":39},"=>\n",[33,1109,1110,1113],{"class":35,"line":382},[33,1111,1112],{"class":47},"    reader.read().toString                  ",[33,1114,1115],{"class":74},"\u002F\u002F reader.close() called automatically\n",[33,1117,1118],{"class":35,"line":388},[33,1119,1120],{"class":47},"  }\n",[33,1122,1123],{"class":35,"line":394},[33,1124,778],{"class":47},[15,1126,1128],{"id":1127},"sam-conversion-java-functional-interfaces-from-scala","SAM Conversion — Java Functional Interfaces from Scala",[20,1130,1131],{"language":22},[24,1132,1134],{"className":26,"code":1133,"language":22,"meta":28,"style":28},"import java.util.function.{Function => JFunction, Predicate => JPredicate, Consumer => JConsumer}\nimport java.util.{List => JList, Map => JMap}\n\n\u002F\u002F Java functional interfaces (Single Abstract Method) accept Scala lambdas directly\nval javaFunction: JFunction[Int, String] = (i: Int) => s\"num=$i\"  \u002F\u002F SAM conversion\nval javaPredicate: JPredicate[String] = _.length > 3\n\n\u002F\u002F Using Java Streams from Scala\nval javaList: JList[Int] = ???  \u002F\u002F from Java API\nval result = javaList.stream()\n  .filter((_: Int) > 5)                     \u002F\u002F Scala lambda → JPredicate (SAM)\n  .map((i: Int) => i * 2)                   \u002F\u002F Scala lambda → JFunction (SAM)\n  .collect(java.util.stream.Collectors.toList())\n\u002F\u002F Result: java.util.List[Int]\n\n\u002F\u002F ❌ ANTI-PATTERN: converting to Scala, processing, converting back\n\u002F\u002F   javaList.asScala.filter(_ > 5).map(_ * 2).asJava\n\u002F\u002F   → 3 collection conversions. Just use the Java Stream API directly.\n\n\u002F\u002F ✅ CORRECT: use Java Streams for Java collections, Scala collections for Scala\n\u002F\u002F Mixing is fine — just avoid unnecessary conversions (each is a copy\u002Fview overhead)\n",[30,1135,1136,1184,1215,1219,1224,1275,1303,1307,1312,1336,1348,1373,1401,1412,1417,1421,1426,1431,1436,1440,1445],{"__ignoreMap":28},[33,1137,1138,1140,1142,1144,1146,1148,1151,1153,1156,1159,1162,1164,1167,1169,1172,1174,1177,1179,1182],{"class":35,"line":36},[33,1139,40],{"class":39},[33,1141,759],{"class":43},[33,1143,48],{"class":47},[33,1145,874],{"class":43},[33,1147,48],{"class":47},[33,1149,1150],{"class":43},"function",[33,1152,767],{"class":47},[33,1154,1155],{"class":43},"Function",[33,1157,1158],{"class":39}," =>",[33,1160,1161],{"class":43}," JFunction",[33,1163,410],{"class":47},[33,1165,1166],{"class":43},"Predicate",[33,1168,1158],{"class":39},[33,1170,1171],{"class":43}," JPredicate",[33,1173,410],{"class":47},[33,1175,1176],{"class":43},"Consumer",[33,1178,1158],{"class":39},[33,1180,1181],{"class":43}," JConsumer",[33,1183,778],{"class":47},[33,1185,1186,1188,1190,1192,1194,1196,1198,1200,1203,1205,1208,1210,1213],{"class":35,"line":64},[33,1187,40],{"class":39},[33,1189,759],{"class":43},[33,1191,48],{"class":47},[33,1193,874],{"class":43},[33,1195,767],{"class":47},[33,1197,237],{"class":43},[33,1199,1158],{"class":39},[33,1201,1202],{"class":43}," JList",[33,1204,410],{"class":47},[33,1206,1207],{"class":43},"Map",[33,1209,1158],{"class":39},[33,1211,1212],{"class":43}," JMap",[33,1214,778],{"class":47},[33,1216,1217],{"class":35,"line":71},[33,1218,68],{"emptyLinePlaceholder":67},[33,1220,1221],{"class":35,"line":78},[33,1222,1223],{"class":74},"\u002F\u002F Java functional interfaces (Single Abstract Method) accept Scala lambdas directly\n",[33,1225,1226,1228,1231,1233,1235,1237,1239,1241,1243,1245,1247,1250,1253,1255,1257,1259,1261,1264,1267,1270,1272],{"class":35,"line":109},[33,1227,81],{"class":39},[33,1229,1230],{"class":84}," javaFunction",[33,1232,247],{"class":39},[33,1234,1161],{"class":43},[33,1236,100],{"class":47},[33,1238,103],{"class":43},[33,1240,410],{"class":47},[33,1242,544],{"class":43},[33,1244,257],{"class":47},[33,1246,161],{"class":39},[33,1248,1249],{"class":47}," (",[33,1251,1252],{"class":84},"i",[33,1254,504],{"class":47},[33,1256,103],{"class":43},[33,1258,158],{"class":47},[33,1260,978],{"class":39},[33,1262,1263],{"class":39}," s",[33,1265,1266],{"class":672},"\"num=",[33,1268,1269],{"class":47},"$i",[33,1271,1016],{"class":672},[33,1273,1274],{"class":74},"  \u002F\u002F SAM conversion\n",[33,1276,1277,1279,1282,1284,1286,1288,1290,1292,1294,1297,1300],{"class":35,"line":128},[33,1278,81],{"class":39},[33,1280,1281],{"class":84}," javaPredicate",[33,1283,247],{"class":39},[33,1285,1171],{"class":43},[33,1287,100],{"class":47},[33,1289,544],{"class":43},[33,1291,257],{"class":47},[33,1293,161],{"class":39},[33,1295,1296],{"class":47}," _.length ",[33,1298,1299],{"class":39},">",[33,1301,1302],{"class":115}," 3\n",[33,1304,1305],{"class":35,"line":133},[33,1306,68],{"emptyLinePlaceholder":67},[33,1308,1309],{"class":35,"line":149},[33,1310,1311],{"class":74},"\u002F\u002F Using Java Streams from Scala\n",[33,1313,1314,1316,1318,1320,1322,1324,1326,1328,1330,1333],{"class":35,"line":170},[33,1315,81],{"class":39},[33,1317,85],{"class":84},[33,1319,247],{"class":39},[33,1321,1202],{"class":43},[33,1323,100],{"class":47},[33,1325,103],{"class":43},[33,1327,257],{"class":47},[33,1329,161],{"class":39},[33,1331,1332],{"class":39}," ???",[33,1334,1335],{"class":74},"  \u002F\u002F from Java API\n",[33,1337,1338,1340,1343,1345],{"class":35,"line":184},[33,1339,81],{"class":39},[33,1341,1342],{"class":84}," result",[33,1344,88],{"class":39},[33,1346,1347],{"class":47}," javaList.stream()\n",[33,1349,1350,1353,1356,1358,1360,1362,1364,1367,1370],{"class":35,"line":189},[33,1351,1352],{"class":47},"  .filter((",[33,1354,1355],{"class":84},"_",[33,1357,504],{"class":47},[33,1359,103],{"class":43},[33,1361,158],{"class":47},[33,1363,1299],{"class":39},[33,1365,1366],{"class":115}," 5",[33,1368,1369],{"class":47},")                     ",[33,1371,1372],{"class":74},"\u002F\u002F Scala lambda → JPredicate (SAM)\n",[33,1374,1375,1378,1380,1382,1384,1386,1388,1391,1393,1395,1398],{"class":35,"line":199},[33,1376,1377],{"class":47},"  .map((",[33,1379,1252],{"class":84},[33,1381,504],{"class":47},[33,1383,103],{"class":43},[33,1385,158],{"class":47},[33,1387,978],{"class":39},[33,1389,1390],{"class":47}," i ",[33,1392,269],{"class":39},[33,1394,272],{"class":115},[33,1396,1397],{"class":47},")                   ",[33,1399,1400],{"class":74},"\u002F\u002F Scala lambda → JFunction (SAM)\n",[33,1402,1403,1406,1409],{"class":35,"line":208},[33,1404,1405],{"class":47},"  .collect(java.util.stream.",[33,1407,1408],{"class":43},"Collectors",[33,1410,1411],{"class":47},".toList())\n",[33,1413,1414],{"class":35,"line":213},[33,1415,1416],{"class":74},"\u002F\u002F Result: java.util.List[Int]\n",[33,1418,1419],{"class":35,"line":219},[33,1420,68],{"emptyLinePlaceholder":67},[33,1422,1423],{"class":35,"line":263},[33,1424,1425],{"class":74},"\u002F\u002F ❌ ANTI-PATTERN: converting to Scala, processing, converting back\n",[33,1427,1428],{"class":35,"line":281},[33,1429,1430],{"class":74},"\u002F\u002F   javaList.asScala.filter(_ > 5).map(_ * 2).asJava\n",[33,1432,1433],{"class":35,"line":287},[33,1434,1435],{"class":74},"\u002F\u002F   → 3 collection conversions. Just use the Java Stream API directly.\n",[33,1437,1438],{"class":35,"line":292},[33,1439,68],{"emptyLinePlaceholder":67},[33,1441,1442],{"class":35,"line":298},[33,1443,1444],{"class":74},"\u002F\u002F ✅ CORRECT: use Java Streams for Java collections, Scala collections for Scala\n",[33,1446,1447],{"class":35,"line":332},[33,1448,1449],{"class":74},"\u002F\u002F Mixing is fine — just avoid unnecessary conversions (each is a copy\u002Fview overhead)\n",[15,1451,1453,1456],{"id":1452},"targetname-clean-jvm-level-names",[30,1454,1455],{},"@targetName"," — Clean JVM-Level Names",[20,1458,1459],{"language":22},[24,1460,1462],{"className":26,"code":1461,"language":22,"meta":28,"style":28},"import scala.annotation.targetName\n\n\u002F\u002F Scala method names can conflict with Java conventions or create ugly bytecode.\n\u002F\u002F @targetName controls the JVM-level method name seen by Java and reflection.\n\nclass StringUtils:\n  @targetName(\"toSnakeCase\")\n  extension (s: String) def `snake-case`: String =  \u002F\u002F backtick names are legal in Scala\n    s.replaceAll(\"([A-Z])\", \"_$1\").toLowerCase\n\n\u002F\u002F From Java: StringUtils.snake_case$extension(\"HelloWorld\") — hard to call\n\u002F\u002F With @targetName: StringUtils.toSnakeCase$extension(\"HelloWorld\") — cleaner\n\n\u002F\u002F Critical for overloaded extension methods with same erasure:\nextension (s: String)\n  @targetName(\"repeatN\") def *(n: Int): String = s * n     \u002F\u002F String * Int\n  @targetName(\"repeatChar\") def *(c: Char): String = s * c  \u002F\u002F String * Char\n\u002F\u002F Without @targetName, both erase to *(String, int) and *(String, char) — no clash\n\u002F\u002F But with generics, erasure can clash. @targetName disambiguates.\n",[30,1463,1464,1480,1484,1489,1494,1498,1509,1522,1551,1567,1571,1576,1581,1585,1590,1605,1649,1692,1697],{"__ignoreMap":28},[33,1465,1466,1468,1470,1472,1475,1477],{"class":35,"line":36},[33,1467,40],{"class":39},[33,1469,44],{"class":43},[33,1471,48],{"class":47},[33,1473,1474],{"class":43},"annotation",[33,1476,48],{"class":47},[33,1478,1479],{"class":43},"targetName\n",[33,1481,1482],{"class":35,"line":64},[33,1483,68],{"emptyLinePlaceholder":67},[33,1485,1486],{"class":35,"line":71},[33,1487,1488],{"class":74},"\u002F\u002F Scala method names can conflict with Java conventions or create ugly bytecode.\n",[33,1490,1491],{"class":35,"line":78},[33,1492,1493],{"class":74},"\u002F\u002F @targetName controls the JVM-level method name seen by Java and reflection.\n",[33,1495,1496],{"class":35,"line":109},[33,1497,68],{"emptyLinePlaceholder":67},[33,1499,1500,1503,1506],{"class":35,"line":128},[33,1501,1502],{"class":39},"class",[33,1504,1505],{"class":43}," StringUtils",[33,1507,1508],{"class":39},":\n",[33,1510,1511,1514,1517,1520],{"class":35,"line":133},[33,1512,1513],{"class":39},"  @",[33,1515,1516],{"class":47},"targetName(",[33,1518,1519],{"class":672},"\"toSnakeCase\"",[33,1521,125],{"class":47},[33,1523,1524,1527,1529,1531,1533,1535,1537,1539,1542,1544,1546,1548],{"class":35,"line":149},[33,1525,1526],{"class":39},"  extension",[33,1528,1249],{"class":47},[33,1530,1007],{"class":84},[33,1532,504],{"class":47},[33,1534,544],{"class":43},[33,1536,158],{"class":47},[33,1538,222],{"class":39},[33,1540,1541],{"class":43}," `snake-case`",[33,1543,247],{"class":39},[33,1545,515],{"class":43},[33,1547,88],{"class":39},[33,1549,1550],{"class":74},"  \u002F\u002F backtick names are legal in Scala\n",[33,1552,1553,1556,1559,1561,1564],{"class":35,"line":170},[33,1554,1555],{"class":47},"    s.replaceAll(",[33,1557,1558],{"class":672},"\"([A-Z])\"",[33,1560,410],{"class":47},[33,1562,1563],{"class":672},"\"_$1\"",[33,1565,1566],{"class":47},").toLowerCase\n",[33,1568,1569],{"class":35,"line":184},[33,1570,68],{"emptyLinePlaceholder":67},[33,1572,1573],{"class":35,"line":189},[33,1574,1575],{"class":74},"\u002F\u002F From Java: StringUtils.snake_case$extension(\"HelloWorld\") — hard to call\n",[33,1577,1578],{"class":35,"line":199},[33,1579,1580],{"class":74},"\u002F\u002F With @targetName: StringUtils.toSnakeCase$extension(\"HelloWorld\") — cleaner\n",[33,1582,1583],{"class":35,"line":208},[33,1584,68],{"emptyLinePlaceholder":67},[33,1586,1587],{"class":35,"line":213},[33,1588,1589],{"class":74},"\u002F\u002F Critical for overloaded extension methods with same erasure:\n",[33,1591,1592,1595,1597,1599,1601,1603],{"class":35,"line":219},[33,1593,1594],{"class":39},"extension",[33,1596,1249],{"class":47},[33,1598,1007],{"class":84},[33,1600,504],{"class":47},[33,1602,544],{"class":43},[33,1604,125],{"class":47},[33,1606,1607,1609,1611,1614,1616,1618,1621,1623,1626,1628,1630,1632,1634,1636,1638,1641,1643,1646],{"class":35,"line":263},[33,1608,1513],{"class":39},[33,1610,1516],{"class":47},[33,1612,1613],{"class":672},"\"repeatN\"",[33,1615,158],{"class":47},[33,1617,222],{"class":39},[33,1619,1620],{"class":43}," *",[33,1622,228],{"class":47},[33,1624,1625],{"class":84},"n",[33,1627,504],{"class":47},[33,1629,103],{"class":43},[33,1631,510],{"class":47},[33,1633,247],{"class":39},[33,1635,515],{"class":43},[33,1637,88],{"class":39},[33,1639,1640],{"class":47}," s ",[33,1642,269],{"class":39},[33,1644,1645],{"class":47}," n     ",[33,1647,1648],{"class":74},"\u002F\u002F String * Int\n",[33,1650,1651,1653,1655,1658,1660,1662,1664,1666,1669,1671,1674,1676,1678,1680,1682,1684,1686,1689],{"class":35,"line":281},[33,1652,1513],{"class":39},[33,1654,1516],{"class":47},[33,1656,1657],{"class":672},"\"repeatChar\"",[33,1659,158],{"class":47},[33,1661,222],{"class":39},[33,1663,1620],{"class":43},[33,1665,228],{"class":47},[33,1667,1668],{"class":84},"c",[33,1670,504],{"class":47},[33,1672,1673],{"class":43},"Char",[33,1675,510],{"class":47},[33,1677,247],{"class":39},[33,1679,515],{"class":43},[33,1681,88],{"class":39},[33,1683,1640],{"class":47},[33,1685,269],{"class":39},[33,1687,1688],{"class":47}," c  ",[33,1690,1691],{"class":74},"\u002F\u002F String * Char\n",[33,1693,1694],{"class":35,"line":287},[33,1695,1696],{"class":74},"\u002F\u002F Without @targetName, both erase to *(String, int) and *(String, char) — no clash\n",[33,1698,1699],{"class":35,"line":292},[33,1700,1701],{"class":74},"\u002F\u002F But with generics, erasure can clash. @targetName disambiguates.\n",[15,1703,1705],{"id":1704},"calling-scala-from-java-the-rough-edges","Calling Scala from Java — The Rough Edges",[20,1707,1708,1902,2025,2029,2380,2384,2611,2615,2762,2766,2781,2794,2806,2810,2830,2853,2877,2887,2921,2925,2936],{"language":22},[24,1709,1711],{"className":26,"code":1710,"language":22,"meta":28,"style":28},"\u002F\u002F Scala code (in com\u002Fexample\u002FModels.scala)\npackage com.example\n\nobject Calculator:\n  def add(a: Int, b: Int): Int = a + b\n  @targetName(\"multiply\") def *(a: Int, b: Int): Int = a * b  \u002F\u002F operator name\n\ncase class User(name: String, age: Int):\n  def greet(): String = s\"Hello, $name\"\n",[30,1712,1713,1718,1731,1735,1745,1789,1838,1842,1875],{"__ignoreMap":28},[33,1714,1715],{"class":35,"line":36},[33,1716,1717],{"class":74},"\u002F\u002F Scala code (in com\u002Fexample\u002FModels.scala)\n",[33,1719,1720,1723,1726,1728],{"class":35,"line":64},[33,1721,1722],{"class":39},"package",[33,1724,1725],{"class":43}," com",[33,1727,48],{"class":47},[33,1729,1730],{"class":43},"example\n",[33,1732,1733],{"class":35,"line":71},[33,1734,68],{"emptyLinePlaceholder":67},[33,1736,1737,1740,1743],{"class":35,"line":78},[33,1738,1739],{"class":39},"object",[33,1741,1742],{"class":43}," Calculator",[33,1744,1508],{"class":39},[33,1746,1747,1750,1753,1755,1758,1760,1762,1764,1767,1769,1771,1773,1775,1778,1780,1783,1786],{"class":35,"line":109},[33,1748,1749],{"class":39},"  def",[33,1751,1752],{"class":43}," add",[33,1754,228],{"class":47},[33,1756,1757],{"class":84},"a",[33,1759,504],{"class":47},[33,1761,103],{"class":43},[33,1763,410],{"class":47},[33,1765,1766],{"class":84},"b",[33,1768,504],{"class":47},[33,1770,103],{"class":43},[33,1772,510],{"class":47},[33,1774,247],{"class":39},[33,1776,1777],{"class":43}," Int",[33,1779,88],{"class":39},[33,1781,1782],{"class":47}," a ",[33,1784,1785],{"class":39},"+",[33,1787,1788],{"class":47}," b\n",[33,1790,1791,1793,1795,1798,1800,1802,1804,1806,1808,1810,1812,1814,1816,1818,1820,1822,1824,1826,1828,1830,1832,1835],{"class":35,"line":128},[33,1792,1513],{"class":39},[33,1794,1516],{"class":47},[33,1796,1797],{"class":672},"\"multiply\"",[33,1799,158],{"class":47},[33,1801,222],{"class":39},[33,1803,1620],{"class":43},[33,1805,228],{"class":47},[33,1807,1757],{"class":84},[33,1809,504],{"class":47},[33,1811,103],{"class":43},[33,1813,410],{"class":47},[33,1815,1766],{"class":84},[33,1817,504],{"class":47},[33,1819,103],{"class":43},[33,1821,510],{"class":47},[33,1823,247],{"class":39},[33,1825,1777],{"class":43},[33,1827,88],{"class":39},[33,1829,1782],{"class":47},[33,1831,269],{"class":39},[33,1833,1834],{"class":47}," b  ",[33,1836,1837],{"class":74},"\u002F\u002F operator name\n",[33,1839,1840],{"class":35,"line":133},[33,1841,68],{"emptyLinePlaceholder":67},[33,1843,1844,1847,1850,1853,1855,1858,1860,1862,1864,1867,1869,1871,1873],{"class":35,"line":149},[33,1845,1846],{"class":39},"case",[33,1848,1849],{"class":39}," class",[33,1851,1852],{"class":43}," User",[33,1854,228],{"class":47},[33,1856,1857],{"class":84},"name",[33,1859,504],{"class":47},[33,1861,544],{"class":43},[33,1863,410],{"class":47},[33,1865,1866],{"class":84},"age",[33,1868,504],{"class":47},[33,1870,103],{"class":43},[33,1872,510],{"class":47},[33,1874,1508],{"class":39},[33,1876,1877,1879,1882,1885,1887,1889,1891,1893,1896,1899],{"class":35,"line":170},[33,1878,1749],{"class":39},[33,1880,1881],{"class":43}," greet",[33,1883,1884],{"class":47},"()",[33,1886,247],{"class":39},[33,1888,515],{"class":43},[33,1890,88],{"class":39},[33,1892,1263],{"class":39},[33,1894,1895],{"class":672},"\"Hello, ",[33,1897,1898],{"class":47},"$name",[33,1900,1901],{"class":672},"\"\n",[20,1903,1905],{"language":1904},"java",[24,1906,1909],{"className":1907,"code":1908,"language":1904,"meta":28,"style":28},"language-java shiki shiki-themes github-light github-dark","\u002F\u002F Java code calling the Scala above\nimport com.example.Calculator;\nimport com.example.User;\nimport com.example.Calculator$;  \u002F\u002F the object's JVM class name\n\npublic class JavaCaller {\n    public static void main(String[] args) {\n        \u002F\u002F Scala object → accessed via MODULE$ singleton\n        int sum = Calculator$.MODULE$.add(2, 3);    \u002F\u002F ugly but works\n        \u002F\u002F OR via the static forwarder (if no class with same name):\n        int sum2 = Calculator.add(2, 3);             \u002F\u002F static forwarder — cleaner\n\n        \u002F\u002F Scala case class → Java sees it as a regular class with apply\u002Funapply\n        User alice = new User(\"Alice\", 30);          \u002F\u002F constructor\n        \u002F\u002F User.apply(\"Alice\", 30) also works via the companion's MODULE$\n        String greeting = alice.greet();\n        String name = alice.name();                  \u002F\u002F Scala 3: name() not name — it's a getter method\n\n        \u002F\u002F Scala * operator → can't call as infix from Java\n        \u002F\u002F int product = Calculator.*(2, 3);          \u002F\u002F illegal Java identifier\n        \u002F\u002F With @targetName: Calculator.multiply(2, 3)  — works!\n    }\n}\n",[30,1910,1911,1916,1921,1926,1931,1935,1940,1945,1950,1955,1960,1965,1969,1974,1979,1984,1989,1994,1998,2003,2011,2016,2021],{"__ignoreMap":28},[33,1912,1913],{"class":35,"line":36},[33,1914,1915],{},"\u002F\u002F Java code calling the Scala above\n",[33,1917,1918],{"class":35,"line":64},[33,1919,1920],{},"import com.example.Calculator;\n",[33,1922,1923],{"class":35,"line":71},[33,1924,1925],{},"import com.example.User;\n",[33,1927,1928],{"class":35,"line":78},[33,1929,1930],{},"import com.example.Calculator$;  \u002F\u002F the object's JVM class name\n",[33,1932,1933],{"class":35,"line":109},[33,1934,68],{"emptyLinePlaceholder":67},[33,1936,1937],{"class":35,"line":128},[33,1938,1939],{},"public class JavaCaller {\n",[33,1941,1942],{"class":35,"line":133},[33,1943,1944],{},"    public static void main(String[] args) {\n",[33,1946,1947],{"class":35,"line":149},[33,1948,1949],{},"        \u002F\u002F Scala object → accessed via MODULE$ singleton\n",[33,1951,1952],{"class":35,"line":170},[33,1953,1954],{},"        int sum = Calculator$.MODULE$.add(2, 3);    \u002F\u002F ugly but works\n",[33,1956,1957],{"class":35,"line":184},[33,1958,1959],{},"        \u002F\u002F OR via the static forwarder (if no class with same name):\n",[33,1961,1962],{"class":35,"line":189},[33,1963,1964],{},"        int sum2 = Calculator.add(2, 3);             \u002F\u002F static forwarder — cleaner\n",[33,1966,1967],{"class":35,"line":199},[33,1968,68],{"emptyLinePlaceholder":67},[33,1970,1971],{"class":35,"line":208},[33,1972,1973],{},"        \u002F\u002F Scala case class → Java sees it as a regular class with apply\u002Funapply\n",[33,1975,1976],{"class":35,"line":213},[33,1977,1978],{},"        User alice = new User(\"Alice\", 30);          \u002F\u002F constructor\n",[33,1980,1981],{"class":35,"line":219},[33,1982,1983],{},"        \u002F\u002F User.apply(\"Alice\", 30) also works via the companion's MODULE$\n",[33,1985,1986],{"class":35,"line":263},[33,1987,1988],{},"        String greeting = alice.greet();\n",[33,1990,1991],{"class":35,"line":281},[33,1992,1993],{},"        String name = alice.name();                  \u002F\u002F Scala 3: name() not name — it's a getter method\n",[33,1995,1996],{"class":35,"line":287},[33,1997,68],{"emptyLinePlaceholder":67},[33,1999,2000],{"class":35,"line":292},[33,2001,2002],{},"        \u002F\u002F Scala * operator → can't call as infix from Java\n",[33,2004,2005,2008],{"class":35,"line":298},[33,2006,2007],{},"        \u002F\u002F int product = Calculator.*(2, 3);",[33,2009,2010],{},"          \u002F\u002F illegal Java identifier\n",[33,2012,2013],{"class":35,"line":332},[33,2014,2015],{},"        \u002F\u002F With @targetName: Calculator.multiply(2, 3)  — works!\n",[33,2017,2018],{"class":35,"line":349},[33,2019,2020],{},"    }\n",[33,2022,2023],{"class":35,"line":365},[33,2024,778],{},[15,2026,2028],{"id":2027},"java-friendly-scala-api-design-rules","Java-Friendly Scala API Design — Rules",[20,2030,2031],{"language":22},[24,2032,2034],{"className":26,"code":2033,"language":22,"meta":28,"style":28},"\u002F\u002F Rules for Scala APIs consumed by Java:\n\u002F\u002F 1. Avoid default parameters — Java can't omit them (must pass full signature)\n\u002F\u002F    ✅ Provide overloaded methods instead\nclass JavaFriendlyRepo:\n  def find(id: Long): Option[User] = find(id, defaultTimeout)  \u002F\u002F calls the full version\n  def find(id: Long, timeoutMs: Long): Option[User] = ???      \u002F\u002F Java can call either\n\n\u002F\u002F 2. Avoid implicit parameters — Java has no equivalent\n\u002F\u002F    ✅ Create an overload that takes the parameter explicitly\ndef query[T](sql: String)(using parser: Parser[T]): Result[T] = ???\ndef queryWithParser[T](sql: String, parser: Parser[T]): Result[T] = query(sql)(using parser)\n\n\u002F\u002F 3. Return Java collections from Java-facing methods — don't force Scala collections on Java\ndef getUsersJava: java.util.List[User] =\n  getUsers.toJava                                \u002F\u002F convert for Java caller\n\n\u002F\u002F 4. Avoid Option in Java-facing APIs — Java doesn't understand it\n\u002F\u002F    Return nullable with @Nullable annotation, or return java.util.Optional\ndef findUserJava(id: Long): java.util.Optional[User] =\n  findUser(id).toJava\n\n\u002F\u002F 5. Case classes work from Java — but use new, not apply\n\u002F\u002F   new User(\"Alice\", 30) — Java can do this\n\u002F\u002F   User.apply(\"Alice\", 30) — only via MODULE$, ugly\n\n\u002F\u002F 6. Sealed traits → Java can't pattern match. Provide a visitor or enum-like API.\n",[30,2035,2036,2041,2046,2051,2060,2096,2138,2142,2147,2152,2210,2264,2268,2273,2294,2302,2306,2311,2316,2347,2352,2356,2361,2366,2371,2375],{"__ignoreMap":28},[33,2037,2038],{"class":35,"line":36},[33,2039,2040],{"class":74},"\u002F\u002F Rules for Scala APIs consumed by Java:\n",[33,2042,2043],{"class":35,"line":64},[33,2044,2045],{"class":74},"\u002F\u002F 1. Avoid default parameters — Java can't omit them (must pass full signature)\n",[33,2047,2048],{"class":35,"line":71},[33,2049,2050],{"class":74},"\u002F\u002F    ✅ Provide overloaded methods instead\n",[33,2052,2053,2055,2058],{"class":35,"line":78},[33,2054,1502],{"class":39},[33,2056,2057],{"class":43}," JavaFriendlyRepo",[33,2059,1508],{"class":39},[33,2061,2062,2064,2067,2069,2071,2073,2075,2077,2079,2081,2083,2086,2088,2090,2093],{"class":35,"line":109},[33,2063,1749],{"class":39},[33,2065,2066],{"class":43}," find",[33,2068,228],{"class":47},[33,2070,501],{"class":84},[33,2072,504],{"class":47},[33,2074,507],{"class":43},[33,2076,510],{"class":47},[33,2078,247],{"class":39},[33,2080,539],{"class":43},[33,2082,100],{"class":47},[33,2084,2085],{"class":43},"User",[33,2087,257],{"class":47},[33,2089,161],{"class":39},[33,2091,2092],{"class":47}," find(id, defaultTimeout)  ",[33,2094,2095],{"class":74},"\u002F\u002F calls the full version\n",[33,2097,2098,2100,2102,2104,2106,2108,2110,2112,2115,2117,2119,2121,2123,2125,2127,2129,2131,2133,2135],{"class":35,"line":128},[33,2099,1749],{"class":39},[33,2101,2066],{"class":43},[33,2103,228],{"class":47},[33,2105,501],{"class":84},[33,2107,504],{"class":47},[33,2109,507],{"class":43},[33,2111,410],{"class":47},[33,2113,2114],{"class":84},"timeoutMs",[33,2116,504],{"class":47},[33,2118,507],{"class":43},[33,2120,510],{"class":47},[33,2122,247],{"class":39},[33,2124,539],{"class":43},[33,2126,100],{"class":47},[33,2128,2085],{"class":43},[33,2130,257],{"class":47},[33,2132,161],{"class":39},[33,2134,1332],{"class":39},[33,2136,2137],{"class":74},"      \u002F\u002F Java can call either\n",[33,2139,2140],{"class":35,"line":133},[33,2141,68],{"emptyLinePlaceholder":67},[33,2143,2144],{"class":35,"line":149},[33,2145,2146],{"class":74},"\u002F\u002F 2. Avoid implicit parameters — Java has no equivalent\n",[33,2148,2149],{"class":35,"line":170},[33,2150,2151],{"class":74},"\u002F\u002F    ✅ Create an overload that takes the parameter explicitly\n",[33,2153,2154,2156,2159,2161,2164,2167,2170,2172,2174,2177,2180,2183,2185,2188,2190,2192,2194,2196,2199,2201,2203,2205,2207],{"class":35,"line":184},[33,2155,222],{"class":39},[33,2157,2158],{"class":43}," query",[33,2160,100],{"class":47},[33,2162,2163],{"class":43},"T",[33,2165,2166],{"class":47},"](",[33,2168,2169],{"class":84},"sql",[33,2171,504],{"class":47},[33,2173,544],{"class":43},[33,2175,2176],{"class":47},")(",[33,2178,2179],{"class":39},"using",[33,2181,2182],{"class":84}," parser",[33,2184,504],{"class":47},[33,2186,2187],{"class":43},"Parser",[33,2189,100],{"class":47},[33,2191,2163],{"class":43},[33,2193,244],{"class":47},[33,2195,247],{"class":39},[33,2197,2198],{"class":43}," Result",[33,2200,100],{"class":47},[33,2202,2163],{"class":43},[33,2204,257],{"class":47},[33,2206,161],{"class":39},[33,2208,2209],{"class":39}," ???\n",[33,2211,2212,2214,2217,2219,2221,2223,2225,2227,2229,2231,2234,2236,2238,2240,2242,2244,2246,2248,2250,2252,2254,2256,2259,2261],{"class":35,"line":189},[33,2213,222],{"class":39},[33,2215,2216],{"class":43}," queryWithParser",[33,2218,100],{"class":47},[33,2220,2163],{"class":43},[33,2222,2166],{"class":47},[33,2224,2169],{"class":84},[33,2226,504],{"class":47},[33,2228,544],{"class":43},[33,2230,410],{"class":47},[33,2232,2233],{"class":84},"parser",[33,2235,504],{"class":47},[33,2237,2187],{"class":43},[33,2239,100],{"class":47},[33,2241,2163],{"class":43},[33,2243,244],{"class":47},[33,2245,247],{"class":39},[33,2247,2198],{"class":43},[33,2249,100],{"class":47},[33,2251,2163],{"class":43},[33,2253,257],{"class":47},[33,2255,161],{"class":39},[33,2257,2258],{"class":47}," query(sql)(",[33,2260,2179],{"class":39},[33,2262,2263],{"class":47}," parser)\n",[33,2265,2266],{"class":35,"line":199},[33,2267,68],{"emptyLinePlaceholder":67},[33,2269,2270],{"class":35,"line":208},[33,2271,2272],{"class":74},"\u002F\u002F 3. Return Java collections from Java-facing methods — don't force Scala collections on Java\n",[33,2274,2275,2277,2280,2282,2284,2286,2288,2290,2292],{"class":35,"line":213},[33,2276,222],{"class":39},[33,2278,2279],{"class":43}," getUsersJava",[33,2281,247],{"class":39},[33,2283,94],{"class":47},[33,2285,237],{"class":43},[33,2287,100],{"class":47},[33,2289,2085],{"class":43},[33,2291,257],{"class":47},[33,2293,260],{"class":39},[33,2295,2296,2299],{"class":35,"line":219},[33,2297,2298],{"class":47},"  getUsers.toJava                                ",[33,2300,2301],{"class":74},"\u002F\u002F convert for Java caller\n",[33,2303,2304],{"class":35,"line":263},[33,2305,68],{"emptyLinePlaceholder":67},[33,2307,2308],{"class":35,"line":281},[33,2309,2310],{"class":74},"\u002F\u002F 4. Avoid Option in Java-facing APIs — Java doesn't understand it\n",[33,2312,2313],{"class":35,"line":287},[33,2314,2315],{"class":74},"\u002F\u002F    Return nullable with @Nullable annotation, or return java.util.Optional\n",[33,2317,2318,2320,2323,2325,2327,2329,2331,2333,2335,2337,2339,2341,2343,2345],{"class":35,"line":292},[33,2319,222],{"class":39},[33,2321,2322],{"class":43}," findUserJava",[33,2324,228],{"class":47},[33,2326,501],{"class":84},[33,2328,504],{"class":47},[33,2330,507],{"class":43},[33,2332,510],{"class":47},[33,2334,247],{"class":39},[33,2336,94],{"class":47},[33,2338,454],{"class":43},[33,2340,100],{"class":47},[33,2342,2085],{"class":43},[33,2344,257],{"class":47},[33,2346,260],{"class":39},[33,2348,2349],{"class":35,"line":298},[33,2350,2351],{"class":47},"  findUser(id).toJava\n",[33,2353,2354],{"class":35,"line":332},[33,2355,68],{"emptyLinePlaceholder":67},[33,2357,2358],{"class":35,"line":349},[33,2359,2360],{"class":74},"\u002F\u002F 5. Case classes work from Java — but use new, not apply\n",[33,2362,2363],{"class":35,"line":365},[33,2364,2365],{"class":74},"\u002F\u002F   new User(\"Alice\", 30) — Java can do this\n",[33,2367,2368],{"class":35,"line":370},[33,2369,2370],{"class":74},"\u002F\u002F   User.apply(\"Alice\", 30) — only via MODULE$, ugly\n",[33,2372,2373],{"class":35,"line":376},[33,2374,68],{"emptyLinePlaceholder":67},[33,2376,2377],{"class":35,"line":382},[33,2378,2379],{"class":74},"\u002F\u002F 6. Sealed traits → Java can't pattern match. Provide a visitor or enum-like API.\n",[15,2381,2383],{"id":2382},"primitive-boxing-auto-conversion","Primitive Boxing — Auto-Conversion",[20,2385,2386],{"language":22},[24,2387,2389],{"className":26,"code":2388,"language":22,"meta":28,"style":28},"\u002F\u002F Scala primitives (Int, Long, Double) ↔ Java boxed types (Integer, Long, Double)\n\u002F\u002F Auto-boxing and unboxing happen automatically, but have performance implications:\n\nval scalaInt: Int = 42\nval javaInteger: java.lang.Integer = scalaInt    \u002F\u002F auto-box: int → Integer (heap allocation)\nval backToInt: Int = javaInteger                 \u002F\u002F auto-unbox: Integer → int (may NPE if null!)\n\n\u002F\u002F ❌ ANTI-PATTERN: storing Scala Ints in Java collections → boxing per element\nval javaList = new java.util.ArrayList[Int]()    \u002F\u002F actually ArrayList[Integer]\njavaList.add(1)                                  \u002F\u002F box: 1 → Integer.valueOf(1)\njavaList.add(2)                                  \u002F\u002F box: 2 → Integer.valueOf(2)\n\u002F\u002F Each add boxes. Each get unboxes. For 1M elements: 1M Integer allocations.\n\n\u002F\u002F ✅ CORRECT: use Scala Array for primitive storage, convert to Java only at boundary\nval scalaArr = Array[Int](1, 2, 3)              \u002F\u002F int[] — no boxing\nval javaArr = scalaArr.map(Integer.valueOf)      \u002F\u002F explicit box at boundary only\n\n\u002F\u002F ⚠️ Java Integer caching: Integer.valueOf(-128..127) returns cached objects (JLS requirement)\n\u002F\u002F   Integer.valueOf(127) == Integer.valueOf(127)  → true (same cached object)\n\u002F\u002F   Integer.valueOf(128) == Integer.valueOf(128)  → MAYBE false (new objects)\n\u002F\u002F   Always use .equals() for Integer comparison, never ==\n",[30,2390,2391,2396,2401,2405,2421,2444,2463,2467,2472,2496,2508,2519,2524,2528,2533,2567,2587,2591,2596,2601,2606],{"__ignoreMap":28},[33,2392,2393],{"class":35,"line":36},[33,2394,2395],{"class":74},"\u002F\u002F Scala primitives (Int, Long, Double) ↔ Java boxed types (Integer, Long, Double)\n",[33,2397,2398],{"class":35,"line":64},[33,2399,2400],{"class":74},"\u002F\u002F Auto-boxing and unboxing happen automatically, but have performance implications:\n",[33,2402,2403],{"class":35,"line":71},[33,2404,68],{"emptyLinePlaceholder":67},[33,2406,2407,2409,2412,2414,2416,2418],{"class":35,"line":78},[33,2408,81],{"class":39},[33,2410,2411],{"class":84}," scalaInt",[33,2413,247],{"class":39},[33,2415,1777],{"class":43},[33,2417,88],{"class":39},[33,2419,2420],{"class":115}," 42\n",[33,2422,2423,2425,2428,2430,2433,2436,2438,2441],{"class":35,"line":109},[33,2424,81],{"class":39},[33,2426,2427],{"class":84}," javaInteger",[33,2429,247],{"class":39},[33,2431,2432],{"class":47}," java.lang.",[33,2434,2435],{"class":43},"Integer",[33,2437,88],{"class":39},[33,2439,2440],{"class":47}," scalaInt    ",[33,2442,2443],{"class":74},"\u002F\u002F auto-box: int → Integer (heap allocation)\n",[33,2445,2446,2448,2451,2453,2455,2457,2460],{"class":35,"line":128},[33,2447,81],{"class":39},[33,2449,2450],{"class":84}," backToInt",[33,2452,247],{"class":39},[33,2454,1777],{"class":43},[33,2456,88],{"class":39},[33,2458,2459],{"class":47}," javaInteger                 ",[33,2461,2462],{"class":74},"\u002F\u002F auto-unbox: Integer → int (may NPE if null!)\n",[33,2464,2465],{"class":35,"line":133},[33,2466,68],{"emptyLinePlaceholder":67},[33,2468,2469],{"class":35,"line":149},[33,2470,2471],{"class":74},"\u002F\u002F ❌ ANTI-PATTERN: storing Scala Ints in Java collections → boxing per element\n",[33,2473,2474,2476,2478,2480,2482,2484,2486,2488,2490,2493],{"class":35,"line":170},[33,2475,81],{"class":39},[33,2477,85],{"class":84},[33,2479,88],{"class":39},[33,2481,91],{"class":39},[33,2483,94],{"class":47},[33,2485,97],{"class":43},[33,2487,100],{"class":47},[33,2489,103],{"class":43},[33,2491,2492],{"class":47},"]()    ",[33,2494,2495],{"class":74},"\u002F\u002F actually ArrayList[Integer]\n",[33,2497,2498,2500,2502,2505],{"class":35,"line":184},[33,2499,112],{"class":47},[33,2501,116],{"class":115},[33,2503,2504],{"class":47},")                                  ",[33,2506,2507],{"class":74},"\u002F\u002F box: 1 → Integer.valueOf(1)\n",[33,2509,2510,2512,2514,2516],{"class":35,"line":189},[33,2511,112],{"class":47},[33,2513,122],{"class":115},[33,2515,2504],{"class":47},[33,2517,2518],{"class":74},"\u002F\u002F box: 2 → Integer.valueOf(2)\n",[33,2520,2521],{"class":35,"line":199},[33,2522,2523],{"class":74},"\u002F\u002F Each add boxes. Each get unboxes. For 1M elements: 1M Integer allocations.\n",[33,2525,2526],{"class":35,"line":208},[33,2527,68],{"emptyLinePlaceholder":67},[33,2529,2530],{"class":35,"line":213},[33,2531,2532],{"class":74},"\u002F\u002F ✅ CORRECT: use Scala Array for primitive storage, convert to Java only at boundary\n",[33,2534,2535,2537,2540,2542,2545,2547,2549,2551,2553,2555,2557,2559,2561,2564],{"class":35,"line":219},[33,2536,81],{"class":39},[33,2538,2539],{"class":84}," scalaArr",[33,2541,88],{"class":39},[33,2543,2544],{"class":43}," Array",[33,2546,100],{"class":47},[33,2548,103],{"class":43},[33,2550,2166],{"class":47},[33,2552,116],{"class":115},[33,2554,410],{"class":47},[33,2556,122],{"class":115},[33,2558,410],{"class":47},[33,2560,194],{"class":115},[33,2562,2563],{"class":47},")              ",[33,2565,2566],{"class":74},"\u002F\u002F int[] — no boxing\n",[33,2568,2569,2571,2574,2576,2579,2581,2584],{"class":35,"line":263},[33,2570,81],{"class":39},[33,2572,2573],{"class":84}," javaArr",[33,2575,88],{"class":39},[33,2577,2578],{"class":47}," scalaArr.map(",[33,2580,2435],{"class":43},[33,2582,2583],{"class":47},".valueOf)      ",[33,2585,2586],{"class":74},"\u002F\u002F explicit box at boundary only\n",[33,2588,2589],{"class":35,"line":281},[33,2590,68],{"emptyLinePlaceholder":67},[33,2592,2593],{"class":35,"line":287},[33,2594,2595],{"class":74},"\u002F\u002F ⚠️ Java Integer caching: Integer.valueOf(-128..127) returns cached objects (JLS requirement)\n",[33,2597,2598],{"class":35,"line":292},[33,2599,2600],{"class":74},"\u002F\u002F   Integer.valueOf(127) == Integer.valueOf(127)  → true (same cached object)\n",[33,2602,2603],{"class":35,"line":298},[33,2604,2605],{"class":74},"\u002F\u002F   Integer.valueOf(128) == Integer.valueOf(128)  → MAYBE false (new objects)\n",[33,2607,2608],{"class":35,"line":332},[33,2609,2610],{"class":74},"\u002F\u002F   Always use .equals() for Integer comparison, never ==\n",[15,2612,2614],{"id":2613},"performance-interop-overhead","Performance — Interop Overhead",[20,2616,2617],{"language":22},[24,2618,2620],{"className":26,"code":2619,"language":22,"meta":28,"style":28},"\u002F\u002F Interop conversion costs (per element):\n\u002F\u002F   asScala \u002F asJava view:   O(1) — no copy, just wrapper\n\u002F\u002F   toList \u002F toVector:       O(n) — full copy\n\u002F\u002F   Option(javaValue):       O(1) — wrapper\n\u002F\u002F   toJava \u002F toJavaOptional: O(1) — wrapper\n\n\u002F\u002F ❌ ANTI-PATTERN: converting back and forth in a hot loop\nfor item \u003C- scalaList do\n  javaApi.process(scalaList.asJava)          \u002F\u002F converts entire list each iteration!\n\u002F\u002F O(n * iterations) conversions — catastrophic for large lists + many iterations\n\n\u002F\u002F ✅ CORRECT: convert once at the boundary, reuse\nval javaList = scalaList.asJava              \u002F\u002F convert once\nfor _ \u003C- 1 to 1000 do\n  javaApi.process(javaList)                  \u002F\u002F reuse the same view — O(1) per call\n\n\u002F\u002F Boxing in generic Java APIs:\n\u002F\u002F java.util.HashMap[Int, String] → HashMap[Integer, String]\n\u002F\u002F Every put\u002Fget boxes\u002Funboxes → 2 allocations per operation\n\u002F\u002F For high-throughput: use primitive-specialized Java libs (Trove, fastutil, HPPC)\n",[30,2621,2622,2627,2632,2637,2642,2647,2651,2656,2673,2681,2686,2690,2695,2709,2730,2738,2742,2747,2752,2757],{"__ignoreMap":28},[33,2623,2624],{"class":35,"line":36},[33,2625,2626],{"class":74},"\u002F\u002F Interop conversion costs (per element):\n",[33,2628,2629],{"class":35,"line":64},[33,2630,2631],{"class":74},"\u002F\u002F   asScala \u002F asJava view:   O(1) — no copy, just wrapper\n",[33,2633,2634],{"class":35,"line":71},[33,2635,2636],{"class":74},"\u002F\u002F   toList \u002F toVector:       O(n) — full copy\n",[33,2638,2639],{"class":35,"line":78},[33,2640,2641],{"class":74},"\u002F\u002F   Option(javaValue):       O(1) — wrapper\n",[33,2643,2644],{"class":35,"line":109},[33,2645,2646],{"class":74},"\u002F\u002F   toJava \u002F toJavaOptional: O(1) — wrapper\n",[33,2648,2649],{"class":35,"line":128},[33,2650,68],{"emptyLinePlaceholder":67},[33,2652,2653],{"class":35,"line":133},[33,2654,2655],{"class":74},"\u002F\u002F ❌ ANTI-PATTERN: converting back and forth in a hot loop\n",[33,2657,2658,2661,2664,2667,2670],{"class":35,"line":149},[33,2659,2660],{"class":39},"for",[33,2662,2663],{"class":47}," item ",[33,2665,2666],{"class":39},"\u003C-",[33,2668,2669],{"class":47}," scalaList ",[33,2671,2672],{"class":39},"do\n",[33,2674,2675,2678],{"class":35,"line":170},[33,2676,2677],{"class":47},"  javaApi.process(scalaList.asJava)          ",[33,2679,2680],{"class":74},"\u002F\u002F converts entire list each iteration!\n",[33,2682,2683],{"class":35,"line":184},[33,2684,2685],{"class":74},"\u002F\u002F O(n * iterations) conversions — catastrophic for large lists + many iterations\n",[33,2687,2688],{"class":35,"line":189},[33,2689,68],{"emptyLinePlaceholder":67},[33,2691,2692],{"class":35,"line":199},[33,2693,2694],{"class":74},"\u002F\u002F ✅ CORRECT: convert once at the boundary, reuse\n",[33,2696,2697,2699,2701,2703,2706],{"class":35,"line":208},[33,2698,81],{"class":39},[33,2700,85],{"class":84},[33,2702,88],{"class":39},[33,2704,2705],{"class":47}," scalaList.asJava              ",[33,2707,2708],{"class":74},"\u002F\u002F convert once\n",[33,2710,2711,2713,2716,2718,2721,2724,2727],{"class":35,"line":213},[33,2712,2660],{"class":39},[33,2714,2715],{"class":47}," _ ",[33,2717,2666],{"class":39},[33,2719,2720],{"class":115}," 1",[33,2722,2723],{"class":47}," to ",[33,2725,2726],{"class":115},"1000",[33,2728,2729],{"class":39}," do\n",[33,2731,2732,2735],{"class":35,"line":219},[33,2733,2734],{"class":47},"  javaApi.process(javaList)                  ",[33,2736,2737],{"class":74},"\u002F\u002F reuse the same view — O(1) per call\n",[33,2739,2740],{"class":35,"line":263},[33,2741,68],{"emptyLinePlaceholder":67},[33,2743,2744],{"class":35,"line":281},[33,2745,2746],{"class":74},"\u002F\u002F Boxing in generic Java APIs:\n",[33,2748,2749],{"class":35,"line":287},[33,2750,2751],{"class":74},"\u002F\u002F java.util.HashMap[Int, String] → HashMap[Integer, String]\n",[33,2753,2754],{"class":35,"line":292},[33,2755,2756],{"class":74},"\u002F\u002F Every put\u002Fget boxes\u002Funboxes → 2 allocations per operation\n",[33,2758,2759],{"class":35,"line":298},[33,2760,2761],{"class":74},"\u002F\u002F For high-throughput: use primitive-specialized Java libs (Trove, fastutil, HPPC)\n",[15,2763,2765],{"id":2764},"tips-tricks","💡 Tips & Tricks",[2767,2768,2769,2776,2777,2780],"p",{},[2770,2771,2772,2775],"strong",{},[30,2773,2774],{},"scala.jdk.CollectionConverters"," (Scala 2.13+ \u002F 3.x)",": Replaces the old ",[30,2778,2779],{},"JavaConverters"," (deprecated). Always import from the new package — the old one has subtle bugs with mutability.",[2767,2782,2783,2789,2790,2793],{},[2770,2784,2785,2788],{},[30,2786,2787],{},"Option(null)"," is the null-safe bridge",": The ",[30,2791,2792],{},"Option.apply"," factory handles null → None automatically. Use it to wrap any Java return value.",[2767,2795,2796,2802,2803,2805],{},[2770,2797,2798,2801],{},[30,2799,2800],{},"@tailrec"," + Java recursion",": If you're calling a recursive Java method from Scala, ",[30,2804,2800],{}," doesn't apply — it only works on Scala-compiled methods. The JVM doesn't guarantee tail-call optimization for Java bytecode.",[15,2807,2809],{"id":2808},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[2767,2811,2812,2825,2826,2829],{},[2770,2813,2814,2817,2818,2821,2822],{},[30,2815,2816],{},"asScala"," on ",[30,2819,2820],{},"java.util.Map"," returns ",[30,2823,2824],{},"mutable.Map",": The view IS mutable — mutations go through to the Java map. If you need immutability, call ",[30,2827,2828],{},".toMap"," to copy.",[2767,2831,2832,2840,2841,2844,2845,2848,2849,2852],{},[2770,2833,2834,2835,2837,2838],{},"Scala ",[30,2836,544],{}," = Java ",[30,2839,544],{},": They're the exact same type (",[30,2842,2843],{},"java.lang.String","). No conversion needed. But Scala adds extension methods (",[30,2846,2847],{},"\"hello\".stripMargin",") via ",[30,2850,2851],{},"StringOps"," — these don't exist in Java.",[2767,2854,2855,2864,2865,2868,2869,2872,2873,2876],{},[2770,2856,2857,2860,2861],{},[30,2858,2859],{},"java.lang.Object"," vs ",[30,2862,2863],{},"scala.AnyRef",": They're the same type. ",[30,2866,2867],{},"AnyRef"," is Scala's name for ",[30,2870,2871],{},"Object",". ",[30,2874,2875],{},"eq"," (reference equality) works on both.",[2767,2878,2879,2882,2883,2886],{},[2770,2880,2881],{},"Default parameter values are erased from Java's perspective",": Java sees the full signature with all parameters. Scala generates a ",[30,2884,2885],{},"$default$N"," method to retrieve defaults, but Java doesn't know to call it. Provide explicit overloads for Java callers.",[2767,2888,2889,504,2898,2900,2901,2903,2904,2906,2907,2909,2910,2912,2913,2916,2917,2920],{},[2770,2890,2834,2891,2894,2895],{},[30,2892,2893],{},"Unit"," ≠ Java ",[30,2896,2897],{},"void",[30,2899,2893],{}," is a type with one value (",[30,2902,1884],{},"). In bytecode, methods returning ",[30,2905,2893],{}," return ",[30,2908,2897],{},". But ",[30,2911,2893],{}," as a type parameter boxes to ",[30,2914,2915],{},"BoxedUnit.INSTANCE"," — avoid ",[30,2918,2919],{},"Future[Unit]"," in Java-facing APIs.",[15,2922,2924],{"id":2923},"quick-quiz","🧠 Quick Quiz",[2767,2926,2927,2928,2931,2932,2935],{},"What happens when you call ",[30,2929,2930],{},"javaList.asScala.toList"," and then mutate ",[30,2933,2934],{},"javaList","?",[2937,2938,2939,2943,2960,3043,3048,3070],"details",{},[2940,2941,2942],"summary",{},"Answer",[2767,2944,2945,2946,2948,2949,2952,2953,2956,2957,48],{},"The ",[30,2947,237],{}," you got is ",[2770,2950,2951],{},"unaffected"," — ",[30,2954,2955],{},".toList"," creates an immutable copy that's independent of the original ",[30,2958,2959],{},"java.util.List",[24,2961,2963],{"className":26,"code":2962,"language":22,"meta":28,"style":28},"val javaList = new java.util.ArrayList[Int]()\njavaList.add(1); javaList.add(2)\n\nval snapshot = javaList.asScala.toList      \u002F\u002F List(1, 2) — immutable COPY\njavaList.add(3)                             \u002F\u002F mutate original\nprintln(snapshot)                           \u002F\u002F → List(1, 2) — unchanged!\nprintln(javaList.asScala.toList)            \u002F\u002F → List(1, 2, 3) — view reflects mutation\n",[30,2964,2965,2985,2997,3001,3015,3027,3035],{"__ignoreMap":28},[33,2966,2967,2969,2971,2973,2975,2977,2979,2981,2983],{"class":35,"line":36},[33,2968,81],{"class":39},[33,2970,85],{"class":84},[33,2972,88],{"class":39},[33,2974,91],{"class":39},[33,2976,94],{"class":47},[33,2978,97],{"class":43},[33,2980,100],{"class":47},[33,2982,103],{"class":43},[33,2984,106],{"class":47},[33,2986,2987,2989,2991,2993,2995],{"class":35,"line":64},[33,2988,112],{"class":47},[33,2990,116],{"class":115},[33,2992,119],{"class":47},[33,2994,122],{"class":115},[33,2996,125],{"class":47},[33,2998,2999],{"class":35,"line":71},[33,3000,68],{"emptyLinePlaceholder":67},[33,3002,3003,3005,3007,3009,3012],{"class":35,"line":78},[33,3004,81],{"class":39},[33,3006,338],{"class":84},[33,3008,88],{"class":39},[33,3010,3011],{"class":47}," javaList.asScala.toList      ",[33,3013,3014],{"class":74},"\u002F\u002F List(1, 2) — immutable COPY\n",[33,3016,3017,3019,3021,3024],{"class":35,"line":109},[33,3018,112],{"class":47},[33,3020,194],{"class":115},[33,3022,3023],{"class":47},")                             ",[33,3025,3026],{"class":74},"\u002F\u002F mutate original\n",[33,3028,3029,3032],{"class":35,"line":128},[33,3030,3031],{"class":47},"println(snapshot)                           ",[33,3033,3034],{"class":74},"\u002F\u002F → List(1, 2) — unchanged!\n",[33,3036,3037,3040],{"class":35,"line":133},[33,3038,3039],{"class":47},"println(javaList.asScala.toList)            ",[33,3041,3042],{"class":74},"\u002F\u002F → List(1, 2, 3) — view reflects mutation\n",[2767,3044,3045,247],{},[2770,3046,3047],{},"The difference",[3049,3050,3051,3061],"ul",{},[3052,3053,3054,3056,3057,3060],"li",{},[30,3055,2816],{}," → a ",[2770,3058,3059],{},"view"," (live, mutations propagate, O(1) creation)",[3052,3062,3063,3056,3066,3069],{},[30,3064,3065],{},"asScala.toList",[2770,3067,3068],{},"copy"," (independent, immutable, O(n) creation)",[2767,3071,3072],{},"Use views when you need live access or zero-copy. Use copies when you need isolation or immutability guarantees.",[3074,3075,3076],"style",{},"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 .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}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 .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);}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}",{"title":28,"searchDepth":64,"depth":64,"links":3078},[3079,3080,3082,3083,3084,3086,3087,3088,3089,3090,3091,3092],{"id":17,"depth":64,"text":18},{"id":446,"depth":64,"text":3081},"Null Handling — Option ↔ Java Optional",{"id":744,"depth":64,"text":745},{"id":1127,"depth":64,"text":1128},{"id":1452,"depth":64,"text":3085},"@targetName — Clean JVM-Level Names",{"id":1704,"depth":64,"text":1705},{"id":2027,"depth":64,"text":2028},{"id":2382,"depth":64,"text":2383},{"id":2613,"depth":64,"text":2614},{"id":2764,"depth":64,"text":2765},{"id":2808,"depth":64,"text":2809},{"id":2923,"depth":64,"text":2924},"md",{},"\u002Fscala\u002F11-java-interoperability",{"title":3097,"description":28},{"Scala — Java Interoperability":3098,"description":3099},"Collection Conversion, Null Safety & Bytecode Boundaries","Production patterns for Scala↔Java interop — scala.jdk.CollectionConverters, Option↔Optional, checked exception handling, SAM conversion, @targetName for clean bytecode, and avoiding the common interop traps.","scala\u002F11-java-interoperability","jSFoz01s9WQqEwxIVe0e_0MpXFdTODPjljjOEIFBFKQ",1789924654183]