por - Scala. ¿Puede una clase de caso con un campo ser una clase de valor?
valor default java (4)
Al menos para "permitir la creación sin nueva sintaxis", puede utilizar métodos u object
antiguos con el método de apply
. toString
tampoco es un problema (si no recuerdo mal), aunque debe definirlo usted mismo si no está utilizando la clase de caso.
Por cierto, el lenguaje permite definir clases de casos que amplían AnyVal
, consulte http://docs.scala-lang.org/overviews/core/value-classes.html
Scala 2.10 introdujo clases de valor . Son muy útiles para escribir código de escritura segura. Además, existen algunas limitaciones, algunas de ellas serán detectadas por el compilador y otras requerirán asignación en tiempo de ejecución.
Quiero crear clases de valor utilizando case class
sintaxis de la case class
, para permitir la creación de una nueva sintaxis y una toString
fácil de usar. No hay coincidencia de patrones, ya que requiere asignación.
Entonces la pregunta es: ¿el uso de case class
sintaxis de case class
requerirá la asignación de la clase de valor?
Consulte la sección de documentación de resumen "Cuando la asignación es necesaria".
Las clases de casos reciben un aviso especial: "Nota: puede usar clases de casos y / o métodos de extensión para una sintaxis más limpia en la práctica".
Pero, como ya dijo @ rudiger-klaehn, el ejemplo de advertencia es proporcionar un AnyVal donde se espera un Any:
package anything
// the caveat from the overview
trait Distance extends Any
case class Meter(val value: Double) extends AnyVal with Distance
class Foo {
def add(a: Distance, b: Distance): Distance = Meter(2.0)
}
object Test extends App {
val foo = new Foo
Console println foo.add(Meter(3.4), Meter(4.3))
}
Mostrando que: javap -app está arreglado en el último 2.11:
scala> :javap -app anything.Test$
public final void delayedEndpoint$anything$Test$1();
flags: ACC_PUBLIC, ACC_FINAL
Code:
stack=7, locals=1, args_size=1
0: aload_0
1: new #63 // class anything/Foo
4: dup
5: invokespecial #64 // Method anything/Foo."<init>":()V
8: putfield #60 // Field foo:Lanything/Foo;
11: getstatic #69 // Field scala/Console$.MODULE$:Lscala/Console$;
14: aload_0
15: invokevirtual #71 // Method foo:()Lanything/Foo;
18: new #73 // class anything/Meter
21: dup
22: ldc2_w #74 // double 3.4d
25: invokespecial #78 // Method anything/Meter."<init>":(D)V
28: new #73 // class anything/Meter
31: dup
32: ldc2_w #79 // double 4.3d
35: invokespecial #78 // Method anything/Meter."<init>":(D)V
38: invokevirtual #84 // Method anything/Foo.add:(Lanything/Distance;Lanything/Distance;)Lanything/Distance;
41: invokevirtual #88 // Method scala/Console$.println:(Ljava/lang/Object;)V
44: return
LocalVariableTable:
Start Length Slot Name Signature
0 45 0 this Lanything/Test$;
LineNumberTable:
line 11: 0
line 12: 11
}
Hay boxeo como nos avisaron.
(Actualización: en realidad, el PR para arreglar -app no se ha fusionado todavía. Manténgase sintonizado).
Para ampliar esta pregunta, Wojciech Langiewicz propone un buen ejemplo de la clase de valor utilizada como clase de caso.
En lugar de:
case class Player(id: Int, gameCash: Int, gameCoins: Int, energy: Int)
Wojciech define:
case class Player(id: PlayerId, gameCash: GameCash, gameCoins: GameCoins, energy: Energy)
con las clases de casos (sin asignación de objetos adicionales en el montón):
case class PlayerId(id: Int) extends AnyVal
case class GameCash(value: Int) extends AnyVal
case class GameCoins(value: Int) extends AnyVal
case class Energy(value: Int) extends AnyVal
Al crear clases de casos que encierran exactamente un parámetro, debe agregar
extends AnyVal
para permitir que el compilador Scala ejecute más optimizaciones. Básicamente, la comprobación de tipos se realizará solo durante la fase de compilación, pero durante el tiempo de ejecución solo se crearán objetos del tipo subyacente, lo que lleva a menos sobrecarga de memoria .La adición de tipos personalizados en los puntos específicos de nuestro código no solo mejoró la legibilidad sino que también nos permitió cometer menos errores: descargue algunas de las comprobaciones que de otra manera tendrían que hacerse en las pruebas (o no hacerlo) en el compilador. También puede ver inmediatamente los errores en su IDE o editor.
Debido a que ahora cada componente de la clase
Player
es en sí mismo un tipo separado, también es muy fácil agregar nuevos operadores que de otra manera probablemente tendrían que agregarse implícitamente, lo que contaminaría un alcance mayor.
Puede tener una clase de caso que es una clase de valor. Como puede ver en el siguiente ejemplo, no hay creación de objetos. Excepto, por supuesto, el inevitable boxeo, si fuera un lanzamiento a Any.
Aquí hay un pedacito de código scala
class ValueClass(val value:Int) extends AnyVal
case class ValueCaseClass(value:Int) extends AnyVal
class ValueClassTest {
var x: ValueClass = new ValueClass(1)
var y: ValueCaseClass = ValueCaseClass(2)
def m1(x:ValueClass) = x.value
def m2(x:ValueCaseClass) = x.value
}
Y el bytecode, que no contiene el menor rastro de las dos clases de valor.
Compiled from "ValueClassTest.scala"
public class ValueClassTest {
public int x();
Code:
0: aload_0
1: getfield #14 // Field x:I
4: ireturn
public void x_$eq(int);
Code:
0: aload_0
1: iload_1
2: putfield #14 // Field x:I
5: return
public int y();
Code:
0: aload_0
1: getfield #21 // Field y:I
4: ireturn
public void y_$eq(int);
Code:
0: aload_0
1: iload_1
2: putfield #21 // Field y:I
5: return
public int m1(int);
Code:
0: iload_1
1: ireturn
public int m2(int);
Code:
0: iload_1
1: ireturn
public rklaehn.ValueClassTest();
Code:
0: aload_0
1: invokespecial #29 // Method java/lang/Object."<init>":()V
4: aload_0
5: iconst_1
6: putfield #14 // Field x:I
9: aload_0
10: iconst_2
11: putfield #21 // Field y:I
14: return
}