E009: 초기 정의 미지원

E009: 초기 정의 미지원 (Early Definitions Not Supported)

이 에러는 얼리 정의(early definitions, 일명 early initializers)를 사용할 때 발생해요. Scala 2에는 있던 기능이지만 Scala 3에서는 더 이상 지원하지 않아요. 대신 트레이트 파라미터(trait parameters)를 쓰세요.

출처: Scala 3 Reference

본문

이전 버전의 Scala는 트레이트 파라미터를 지원하지 않아서, 슈퍼클래스 생성자가 실행되기 전에 값을 초기화하기 위한 대안으로 "얼리 정의"(일명 "초기 정의"/early initializers)를 사용했어요.

Scala 3에서는 트레이트 파라미터가 이 문제에 더 깔끔한 해법을 제공해요.

Example

trait Logging:
  val logFile: String
  println(s"Logging to $logFile")

class App extends { val logFile = "app.log" } with Logging

Error

-- Error: example.scala:5:18 ---------------------------------------------------
5 |class App extends { val logFile = "app.log" } with Logging
  |                  ^
  |                  `extends` must be followed by at least one parent
-- [E009] Syntax Error: example.scala:5:46 -------------------------------------
5 |class App extends { val logFile = "app.log" } with Logging
  |                                              ^^^^
  |         Early definitions are not supported; use trait parameters instead
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  | Earlier versions of Scala did not support trait parameters and "early
  | definitions" (also known as "early initializers") were used as an alternative.
  |
  | Example of old syntax:
  |
  | trait Logging {
  |   val f: File
  |   f.open()
  |   onExit(f.close())
  |   def log(msg: String) = f.write(msg)
  | }
  |
  | class B extends Logging {
  |   val f = new File("log.data") // triggers a NullPointerException
  | }
  |
  | // early definition gets around the NullPointerException
  | class C extends {
  |   val f = new File("log.data")
  | } with Logging
  |
  | The above code can now be written as:
  |
  | trait Logging(f: File) {
  |   f.open()
  |   onExit(f.close())
  |   def log(msg: String) = f.write(msg)
  | }
  |
  | class C extends Logging(new File("log.data"))
   -----------------------------------------------------------------------------
-- Error: example.scala:5:51 ---------------------------------------------------
5 |class App extends { val logFile = "app.log" } with Logging
  |                                                   ^^^^^^^
  |                  end of toplevel definition expected but identifier found

Solution

// Use trait parameters instead of early definitions
trait Logging(logFile: String):
  println(s"Logging to $logFile")

class App extends Logging("app.log")
// Alternative: Use a lazy val to defer initialization
trait Logging:
  def logFile: String
  lazy val logger = s"Logging to $logFile"

class App extends Logging:
  val logFile = "app.log"

더 알아보기

  • 트레이트 파라미터에 대한 자세한 내용은 "Traits" 섹션을 참고하세요.
  • 초기화 순서 이슈에는 lazy val도 유용한 대안이에요.