val y objeto dentro de una clase scala?

¿Cuál es la diferencia entre declarar un campo como val , lazy val y object dentro de una clase scala, como en el siguiente fragmento de código:

 class A class B { val a1 = new A { def foo = 1 } object a2 extends A { def foo = 1 } lazy val a3 = new A { def foo = 1 } } 

En el primero, cualquier código incluido se ejecuta tan pronto como se crea la clase B. En este último caso, sin embargo, hasta que realmente use el objeto, no se creará una instancia.

Puedes ver la diferencia aquí:

 class A { println("Creating a new A") } class B { val a1 = new A { println("a1"); def foo = 1 } object a2 extends A { println("a2"); def foo = 1 } } scala> val b = new B Creating a new A a1 b: B = B@1176e8a scala> b.a2.foo Creating a new A a2 res0: Int = 1 

También hay diferencias ocultas en lo que los archivos .class creados son nombrados y tal; y por supuesto, los dos tienen diferentes tipos.

Una diferencia importante es que val’s puede ser anulado mientras que los objetos no pueden.

 class C extends B { override val a1 = new A { def foo = 2 } override object a2 extends A { def foo = 2 } } 

lleva a:

 :9: error: overriding object a2 in class B of type object C.this.a2; object a2 cannot be used here - classes and objects cannot be overridden override object a2 extends A { def foo = 2 } 

No estoy seguro de que alguien haya reconocido el significado de su respuesta , pero los diferentes tipos en realidad representan una diferencia crítica entre vals y objects . En particular, val y lazy val tienen un tipo estructural (p. Ej. A{def foo: Int} ), mientras que el object tiene un tipo singleton. Como resultado, las llamadas al método foo en los val implican la reflexión, mientras que las llamadas al método foo en el object no:

 class A class B { val a1 = new A { def foo = printStack } object a2 extends A { def foo = printStack } lazy val a3 = new A { def foo = printStack } def printStack() = new Exception().getStackTrace take 3 foreach println } scala> val b = new B b: B = B@5c750 scala> b.a1.foo // the val line124$object$$iw$$iw$B.printStack(:12) line124$object$$iw$$iw$B$$anon$1.foo(:7) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) scala> b.a2.foo // the object line124$object$$iw$$iw$B.printStack(:12) line124$object$$iw$$iw$B$a2$.foo(:8) line128$object$$iw$$iw$.(:9) scala> b.a3.foo // the lazy val line124$object$$iw$$iw$B.printStack(:12) line124$object$$iw$$iw$B$$anon$2.foo(:9) sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 

Supongo que una diferencia es que a1 será de un subtipo de A mientras que a2 será de otro subtipo de A saber, a2.type .

 scala> class A defined class A scala> val a1 = new A {def foo = 1} a1: A{def foo: Int} = $anon$1@a9db0e2 scala> object a2 extends A {def foo = 1} defined module a2 scala> a1 res0: A{def foo: Int} = $anon$1@a9db0e2 scala> a2 res1: a2.type = a2$@5b25d568 scala> 

Otra gran diferencia es que los objetos conocen su propio nombre y Val no.

La primera diferencia práctica es que los objetos y vagos perezosos son flojos, mientras que los vals están ansiosos.

La principal diferencia entre objetos y valores perezosos es que un objeto es, desde la perspectiva de los idiomas considerado como un “singleton”, que desde la perspectiva de jvm generalmente se trata como un miembro estático. La definición del objeto en el ejemplo dado no puede ser anulada, como otros han demostrado, por la misma razón que los miembros estáticos no pueden ser reemplazados: sin estar atados a una instancia, no hay forma concebible de hacer una búsqueda de función virtual.

 object Foo { object Bar extends A; } 

es vagamente parecido al siguiente código de Java:

 class Foo { private static class Bar extends A{} public static Bar Bar = new Bar; } 

Si en el ejemplo anterior, si se definiera una subclase C, Foo no podría anular la definición de Bar. Se accederá a la barra de instancias estáticas en Java como Foo.Bar. C.Bar no significa lo mismo que (nueva C) .Bar. Puede que esté un poco equivocado, en realidad no he intentado descifrar el código scala, esto es solo un ejemplo para ilustrar el concepto general de objetos como miembros estáticos.

perezoso vals puede ser un poco menos eficiente. La última vez que revisé, se implementaron al mantener un campo oculto en la clase que mantenía un registro de los valores perezosos que se habían inicializado. Mantener este campo requiere locking, lo que puede causar problemas de rendimiento.

Una diferencia práctica importante entre vago val y objeto es el tratamiento de la falla:

Si tengo:

 class Foo() { throw new Exception("blah!"); } object Stuff { object Bar extends Foo { val x = "hi" } } Stuff.Bar //exception "blah!" thrown. Stuff.Bar.x //NoClassDefFoundError: Could not initialize Stuff$Bar$ 

mientras que si lo hago:

 object Stuff2 { lazy val Bar = new Foo() { val x = "hi" } } Stuff2.Bar // "blah!" Stuff2.Bar.x // "blah!" 

El “NoClassDefFoundError” puede ser realmente confuso, y como es un error, no una excepción, puede romper el código de manejo de errores que (apropiadamente) atrapa / registra “Excepción” pero permite la propagación de errores. Incluso podría considerar este tipo de error en el lenguaje Scala, ya que este caso de uso indica una condición excepcional, no un verdadero error de JVM. He visto NoClassDefFoundErrors al acceder a objetos que dependían de recursos externos (por ejemplo, conexiones de bases de datos o archivos en el disco). Solo el primer acceso registra la causa subyacente, por lo que la correcta depuración de dicho problema generalmente requiere el reinicio de su servidor de aplicaciones.

Este no es un tipo estructural: val a = new A {def foo = 1}

Crea una subclase anónima única; a.foo invoca foo en esa clase.

x aquí hay un tipo de estructura: def bar (x: {def bass: Int})

x.bass introspectará x (de tipo desconocido) para encontrar un método con el nombre ‘bajo’. Funcionará con peces o instrumentos musicales. 😉

Una diferencia entre el objeto perezoso y el objeto es esta:

 var someA = (new B).a3 someA = (new B).a3 // ok var anotherA = (new B).a2 anotherA = = (new B).a2 // compile error