A defpuede implementarse mediante a def, a val, a lazy valo an object. Entonces es la forma más abstracta de definir un miembro. Dado que los rasgos suelen ser interfaces abstractas, decir que quieres una vales decir cómo debería funcionar la implementación. Si solicita a val, una clase de implementación no puede usar a def.
A valsolo es necesario si necesita un identificador estable, por ejemplo, para un tipo dependiente de la ruta. Eso es algo que normalmente no necesitas.
Comparar:
trait Foo { def bar: Int }
object F1 extends Foo { def bar = util.Random.nextInt(33) }
class F2(val bar: Int) extends Foo // ok
object F3 extends Foo {
lazy val bar = {
Thread.sleep(5000)
42
}
}
Si tuvieras
trait Foo { val bar: Int }
no podrías definir F1o F3.
Ok, y para confundirte y responder @ om-nom-nom, el uso de vals abstractos puede causar problemas de inicialización:
trait Foo {
val bar: Int
val schoko = bar + bar
}
object Fail extends Foo {
val bar = 33
}
Fail.schoko
Este es un problema feo que, en mi opinión personal, debería desaparecer en futuras versiones de Scala al arreglarlo en el compilador, pero sí, actualmente esta es también una razón por la que uno no debería usar vals abstractos .
Editar (enero de 2016): se le permite anular una valdeclaración abstracta con una lazy valimplementación, por lo que también evitaría el error de inicialización.
valcon unlazy val. Su afirmación de que no podría crearF3sibarfuera unvalno es correcta. Dicho esto, los miembros abstractos en rasgos siempre deben serdef'sval schoko = bar + barconlazy val schoko = bar + bar. Esa es una forma de tener cierto control sobre el orden de inicialización. Además, usar enlazy vallugar dedefen la clase derivada evita el recálculo.val bar: Intadef bar: IntFail.schokosigue siendo cero.Prefiero no usar
valen rasgos porque la declaración de val tiene un orden de inicialización poco claro y no intuitivo. Puede agregar un rasgo a la jerarquía que ya funciona y rompería todas las cosas que funcionaron antes, vea mi tema: por qué usar val simple en clases no finalesDebe tener en cuenta todo lo relacionado con el uso de estas declaraciones val que eventualmente lo conducen a un error.
Actualizar con un ejemplo más complicado
Pero hay momentos en los que no puede evitar el uso
val. Como @ 0__ mencionó, a veces necesitas un identificador estable ydefno lo es.Daría un ejemplo para mostrar de qué estaba hablando:
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 }Este código produce el error:
StableIdentifier.scala:14: error: stable identifier required, but Access2.this.holder found. def access : holder.Inner =Si se toma un minuto para pensar, comprenderá que el compilador tiene una razón para quejarse. En el
Access2.accesscaso, no podría derivar el tipo de retorno de ninguna manera.def holdersignifica que podría implementarse de manera amplia. Podría devolver diferentes titulares para cada llamada y los titulares incorporarían diferentesInnertipos. Pero la máquina virtual Java espera que se devuelva el mismo tipo.fuente
Usar siempre def parece un poco incómodo ya que algo como esto no funcionará:
trait Entity { def id:Int} object Table { def create(e:Entity) = {e.id = 1 } }Obtendrá el siguiente error:
error: value id_= is not a member of Entityfuente
var. El caso es que, si son campos, deberían designarse como tales. Creo que tener todo comodefes miope.varpermite romper la encapsulación. Pero se prefiere usar adef(o aval) sobre una variable global. Creo que lo que estás buscando es algocase class ConcreteEntity(override val id: Int) extends Entityasí como para poder crearlo desde.def create(e: Entity) = ConcreteEntity(1)Esto es más seguro que romper la encapsulación y permitir que cualquier clase cambie de Entidad.