Когда использовать 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
val
s может вызвать проблемы с инициализацией:trait Foo { val bar: Int val schoko = bar + bar } object Fail extends Foo { val bar = 33 } Fail.schoko // zero!!
это уродливая проблема, которая, по моему личному мнению, должна уйти в будущих версиях Scala, исправив ее в компиляторе, Но да, в настоящее время это также причина, по которой не следует использовать abstract
val
s.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.access
case он никак не мог получить тип возвращаемого значения.def holder
означает, что он может быть реализован в широком смысле. Он может возвращать разные держатели для каждого вызова, и что держатели будут включать разныеInner
типы. Но виртуальная машина Java ожидает, что будет возвращен тот же тип.