Когда использовать val или def в чертах Scala?
Я шел через  эффективные слайды scala и он упоминает на слайде 10, чтобы никогда не использовать val на trait для абстрактных членов и использовать def вместо. Слайд не упоминает подробно, почему с помощью абстрактного val на trait - это анти-паттерн. Я был бы признателен, если кто-то может объяснить лучшую практику использования val vs def в черте для абстрактных методов 
3 ответа:
A
defможет быть реализован либоdef, aval, alazy valилиobject. Так что это самая абстрактная форма определения члена. Поскольку черты обычно являются абстрактными интерфейсами, говоря, что вы хотитеvalговорит как реализация должна сделать. Если вы проситеval, реализующий класс не может использоватьdef.A
valтребуется только в том случае, если вам нужен стабильный идентификатор, например, для типа, зависящего от пути. Это то, что вы обычно не нужно.
сравниваем:
trait Foo { def bar: Int } object F1 extends Foo { def bar = util.Random.nextInt(33) } // ok class F2(val bar: Int) extends Foo // ok object F3 extends Foo { lazy val bar = { // ok Thread.sleep(5000) // really heavy number crunching 42 } }если у вас
trait Foo { val bar: Int }вы не сможете определить
F1илиF3.
ок, а чтобы запутать вас и ответить @om-nom-nom-using abstract
vals может вызвать проблемы с инициализацией:trait Foo { val bar: Int val schoko = bar + bar } object Fail extends Foo { val bar = 33 } Fail.schoko // zero!!это уродливая проблема, которая, по моему личному мнению, должна уйти в будущих версиях Scala, исправив ее в компиляторе, Но да, в настоящее время это также причина, по которой не следует использовать abstract
vals.Edit (Jan 2016): вы можете переопределить абстрактный
valдекларацииlazy valреализация, так что бы также предотвратить сбой инициализации.
Я предпочитаю не использовать
valв чертах, потому что объявление val имеет неясный и неинтуитивный порядок инициализации. Вы можете добавить черту в уже работающую иерархию, и это сломает все, что работало раньше, см. мою тему: зачем использовать простой val в нефинансовых классахвы должны держать все вещи об использовании этого объявления val в виду, что в конечном итоге приведет вас к ошибке.
обновление с более сложными пример
но бывают случаи, когда вы не могли избежать использования
val. Как @0__ упоминалось иногда вам нужен стабильный идентификатор иdefне один.Я бы привел пример, чтобы показать, о чем он говорил:
trait Holder { type Inner val init : Inner } class Access(val holder : Holder) { val access : holder.Inner = holder.init } trait Access2 { def holder : Holder def access : holder.Inner = holder.init }этот код выдает ошибку:
StableIdentifier.scala:14: error: stable identifier required, but Access2.this.holder found. def access : holder.Inner =если вы потратите минуту, чтобы подумать, вы поймете, что у компилятора есть причина жаловаться. В
Access2.accesscase он никак не мог получить тип возвращаемого значения.def holderозначает, что он может быть реализован в широком смысле. Он может возвращать разные держатели для каждого вызова, и что держатели будут включать разныеInnerтипы. Но виртуальная машина Java ожидает, что будет возвращен тот же тип.