최상위 정의
최상위 정의 (Top-level Definitions)
Scala 3에 오면서 소스 파일의 가장 바깥, 즉 **최상위(top level)**에 바로 정의를 쓸 수 있게 됐어요. 패키지나 클래스 안에 감싸지 않아도 돼서, 코드가 훨씬 간결해져요. 실제로 어떤 형태인지 예시부터 볼게요.
출처: Scala 3 Book
본문
Scala 3에서는 모든 종류의 정의를 소스 파일의 최상위에 작성할 수 있어요. 예를 들어 MyCoolApp.scala라는 파일을 만들어서 이런 내용을 넣어볼 수 있죠:
import scala.collection.mutable.ArrayBuffer
enum Topping:
case Cheese, Pepperoni, Mushrooms
import Topping.*
class Pizza:
val toppings = ArrayBuffer[Topping]()
val p = Pizza()
extension (s: String)
def capitalizeAllWords = s.split(" ").map(_.capitalize).mkString(" ")
val hwUpper = "hello, world".capitalizeAllWords
type Money = BigDecimal
// more definitions here as desired ...
@main def myApp =
p.toppings += Cheese
println("show me the code".capitalizeAllWords)
보시다시피, 이런 정의들을 굳이 package, class 같은 구조 안에 넣을 필요가 없어요. 그냥 파일 최상위에 바로 정의하면 되죠.
패키지 객체(package objects)를 대체해요
Scala 2에 익숙하다면, 이 방식이 기존의 **패키지 객체(package objects)**를 대체한다고 보면 돼요. 사용법은 훨씬 쉬워졌지만 원리는 비슷해요. foo라는 이름의 패키지에 정의를 하나 두면, foo 아래의 모든 하위 패키지(예를 들어 foo.bar 패키지)에서 그 정의에 접근할 수 있어요:
package foo {
def double(i: Int) = i * 2
}
package foo {
package bar {
@main def fooBarMain =
println(s"${double(1)}") // this works
}
}
이 예시에서는 패키지 중첩 관계를 강조하기 위해 중괄호({})를 사용했어요.
이 방식의 장점은 다음과 같아요. com.acme.myapp이라는 패키지 아래에 정의를 두면, 그 정의를 com.acme.myapp.model, com.acme.myapp.controller 등에서 자유롭게 참조할 수 있게 됩니다.