Как определяются final val внутри типажа, обрабатываемого компилятором Scala?

Я очень часто использую шаблон трейта selfless, и мне нужно использовать "дорогие" константы внутри трейта: я хотел бы иметь один экземпляр этих значений, который может потребовать нескольких шагов для вычисления, в все мое приложение.

Однако паттерн черты «самоотверженность» приводит к следующему дизайну:

  • Черта MyStuff
  • Объект MyStuff расширяет MyStuff

Очевидно, что размещение констант внутри объекта и использование их внутри трейта создает циклическую зависимость. Однако размещение их в трейте позволяет всем классам, расширяющим трейт, переопределять их, и поэтому они, конечно, не являются синглтоном для всего приложения.

Является ли компилятор Scala «достаточно умным», чтобы сделать final vals внутри типажа «старым общедоступным статическим окончательным java»?


person Edmondo1984    schedule 06.12.2013    source источник


Ответы (3)


Нет, scala не будет переводить final val внутри трейта в эквивалент static final java, потому что final val должен быть членом экземпляра (а не статическим членом) наследующего класса.

scala> trait myStuff { final val Test="test" }
defined trait myStuff

scala> class t extends myStuff
defined class t

scala> t.Test
<console>:8: error: not found: value t
              t.Test
              ^

// we need an instance of t to access Test
scala> new t
res2: t = t@35612600

scala> res2.Test
res3: String = test

если вы используете selfless trait и не можете сохранить свой final val в объекте-компаньоне MyStuff (поскольку вы используете его в самом признаке), вы, вероятно, могли бы просто создать другой объект для вашего final val.

//your expensive constant is here
scala> object MyConstant {final val C="C"}
defined module MyConstant

scala> :paste
// Entering paste mode (ctrl-D to finish)

trait MyStuff {
import MyConstant._
def doStuff = C
}
object MyStuff extends MyStuff

// Exiting paste mode, now interpreting.

defined trait MyStuff
defined module MyStuff

// let's test importing the companion object of the selfless trait 
scala> import MyStuff._
import MyStuff._

scala> doStuff
res4: String = C
person Paolo Falabella    schedule 06.12.2013
comment
Чтобы получить доступ к Test без экземпляра t: val mine: myStuff = null; mine.Test. - person som-snytt; 06.12.2013

Какой пример циклической зависимости вас беспокоит?

Обычно это решается соответствующим использованием defs в трейте или lazy vals.

Вот пример проблемы, вызванной аргументами по умолчанию (которые синтезируются в сопутствующем объект).

Но если вам нужно рвение, вы всегда можете определить заранее, définition en avance:

scala> :pa
// Entering paste mode (ctrl-D to finish)

trait Foo {
  val v = 2 * Foo.w
}
object Foo extends {
  private val w = 3
} with Foo

// Exiting paste mode, now interpreting.

defined trait Foo
defined object Foo

scala> Foo.v
res11: Int = 6

Однако если при вычислении w используются элементы Foo, вам придется полениться:

trait Foo {
  val v = 2 * Foo.w
  def x = 7
}
object Foo extends Foo {
  lazy val w = 3 * x
}

Сегодня второй раз мне нужен FAQ с одним вопросом, но я еще не искал его новый дом.

(Изменить: почему, вот оно.)

person som-snytt    schedule 06.12.2013

В качестве аналога public static final вы должны использовать такой сопутствующий объект:

trait MyStuff
object MyStuff {
 val publicStaticFinal = ...
}

В этом случае scalac создает одноэлементный объект (public static final MyStuff$ MODULE$) с помощью метода public int publicStaticFinal(). Вы можете сделать этот метод final, если хотите.

Для public final используйте final val:

trait MyStuff
 final val publicFinal = ...
}

В этом случае scalac создает интерфейс с public abstract int publicFinal() и реализует его в каждом предке MyStuff как public final int publicFinal().

person senia    schedule 06.12.2013