prejuicio implícito implicitas implicita explícito ejemplos discriminacion actitudes scala inheritance traits

implícito - ¿Cuándo usar val o def en los rasgos de Scala?



ejemplos de implícito y explícito (3)

Estaba revisando las diapositivas scala efectivas y menciona en la diapositiva 10 que nunca use val en un trait para miembros abstractos y use def lugar. La diapositiva no menciona en detalle por qué el uso de val abstracto en un trait es un antipatrón. Apreciaría si alguien puede explicar las mejores prácticas en torno al uso de val vs def en un rasgo de métodos abstractos


Prefiero no usar val en los rasgos porque la declaración val tiene un orden de inicialización confuso y no intuitivo. Puede agregar un rasgo a la jerarquía que ya funciona y rompería todo lo que funcionó antes, vea mi tema: por qué usar plain val en clases no finales

Debes tener en cuenta todo lo relacionado con el uso de estas val declaraciones en mente, que eventualmente te llevarán a un error.

Actualización con un ejemplo más complicado

Pero a veces no puedes evitar usar val . Como @ 0__ había mencionado, a veces necesita un identificador estable y def no es uno.

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 que entenderá que el compilador tiene un motivo para quejarse. En el caso de Access2.access no pudo derivar el tipo de devolución de ninguna manera. def holder significa que podría implementarse de manera amplia. Podría devolver diferentes titulares para cada llamada y los titulares incorporarían diferentes tipos Inner . Pero la máquina virtual Java espera que se devuelva el mismo tipo.


Una def puede implementarse mediante un def , un val , un lazy val o un object . Entonces, es la forma más abstracta de definir un miembro. Dado que los rasgos suelen ser interfaces abstractas, decir que quiere un val es decir cómo debería hacerlo la implementación. Si solicita un val , una clase implementadora no puede usar una def .

Se necesita un valor solo si necesita un identificador estable, por ejemplo, para un tipo dependiente de la ruta. Eso es algo que usualmente no necesitas.

Comparar:

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 } }

Si tuvieras

trait Foo { val bar: Int }

no podrías definir F1 o F3 .

Ok, y confundirlo y responder a los val abstractos de @ om-nom-nom puede causar problemas de inicialización:

trait Foo { val bar: Int val schoko = bar + bar } object Fail extends Foo { val bar = 33 } Fail.schoko // zero!!

Este es un problema desagradable que, en mi opinión personal, debería desaparecer en futuras versiones de Scala, corrigiéndolo en el compilador, pero sí, actualmente esta también es una razón por la cual no se deben usar val abstractos.

Editar (enero de 2016): se le permite anular una declaración val abstracta con una implementación de lazy val , por lo que también evitaría la falla de inicialización.


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 Entity