2010-02-22 22 views
360

He buscado en Google para encontrar las diferencias entre un case class y un class. Todo el mundo menciona que cuando desee hacer una coincidencia de patrones en la clase, use la clase de casos. De lo contrario, use las clases y mencione algunas ventajas adicionales, como la anulación de los códigos hash y equals. Pero, ¿son estas las únicas razones por las que uno debería usar una clase de caso en lugar de una clase?¿Cuál es la diferencia entre la clase de caso y clase de Scala?

Supongo que debería haber una razón muy importante para esta función en Scala. ¿Cuál es la explicación o hay un recurso para aprender más sobre las clases de casos de Scala?

Respuesta

318

Las clases de casos se pueden ver como objetos de soporte de datos simples e inmutables que deberían depender exclusivamente de sus argumentos de constructor.

Este concepto funcional nos permite

  • utilizar una sintaxis de inicialización compacto (Node(1, Leaf(2), None)))
  • las descomponen utilizando coincidencia de patrones
  • tener comparaciones de igualdad definido implícitamente

En combinación con la herencia , las clases de casos se usan para imitar algebraic datatypes.

Si un objeto realiza cálculos con estado en el interior o muestra otros tipos de comportamiento complejo, debe ser una clase ordinaria.

+0

@Dario Gracias por los punteros. ¿Entonces los ADT son algo así como los Enums? –

+9

@Teja: De alguna manera. Los ADT son enum * enumeraciones parametrizadas *, extremadamente potentes y seguras en cuanto a tipo de letra. – Dario

+7

Las clases de casos sellados se utilizan para imitar los tipos de datos algebraicos. De lo contrario, el número de subclases no está limitado. –

50
  • clases de caso pueden ser patrón coincide
  • clases de casos definen automáticamente código hash y es igual a
  • clases de casos definen automáticamente métodos getter para los argumentos de constructor.

(Usted ya mencionó todos menos el último).

Esas son las únicas diferencias con respecto a las clases normales.

+10

Los setters no se generan para las clases de casos a menos que se especifique "var" en el argumento constructor, en cuyo caso obtendrá la misma generación de getter/setter que las clases normales. –

+1

@Mitch: Es cierto, mi mal. Corregido ahora. – sepp2k

+0

Has omitido 2 diferencias, mira mi respuesta. –

144

Técnicamente, no hay diferencia entre una clase y una clase de caso, incluso si el compilador optimiza algunas cosas al usar clases de casos. Sin embargo, una clase de caso se utiliza para eliminar la placa de la caldera para un patrón específico, que está implementando algebraic data types.

Un ejemplo muy simple de tales tipos son los árboles. Un árbol binario, por ejemplo, se puede implementar de esta manera:

sealed abstract class Tree 
case class Node(left: Tree, right: Tree) extends Tree 
case class Leaf[A](value: A) extends Tree 
case object EmptyLeaf extends Tree 

que nos permiten hacer lo siguiente:

// DSL-like assignment: 
val treeA = Node(EmptyLeaf, Leaf(5)) 
val treeB = Node(Node(Leaf(2), Leaf(3)), Leaf(5)) 

// On Scala 2.8, modification through cloning: 
val treeC = treeA.copy(left = treeB.left) 

// Pretty printing: 
println("Tree A: "+treeA) 
println("Tree B: "+treeB) 
println("Tree C: "+treeC) 

// Comparison: 
println("Tree A == Tree B: %s" format (treeA == treeB).toString) 
println("Tree B == Tree C: %s" format (treeB == treeC).toString) 

// Pattern matching: 
treeA match { 
    case Node(EmptyLeaf, right) => println("Can be reduced to "+right) 
    case Node(left, EmptyLeaf) => println("Can be reduced to "+left) 
    case _ => println(treeA+" cannot be reduced") 
} 

// Pattern matches can be safely done, because the compiler warns about 
// non-exaustive matches: 
def checkTree(t: Tree) = t match { 
    case Node(EmptyLeaf, Node(left, right)) => 
    // case Node(EmptyLeaf, Leaf(el)) => 
    case Node(Node(left, right), EmptyLeaf) => 
    case Node(Leaf(el), EmptyLeaf) => 
    case Node(Node(l1, r1), Node(l2, r2)) => 
    case Node(Leaf(e1), Leaf(e2)) => 
    case Node(Node(left, right), Leaf(el)) => 
    case Node(Leaf(el), Node(left, right)) => 
    // case Node(EmptyLeaf, EmptyLeaf) => 
    case Leaf(el) => 
    case EmptyLeaf => 
} 

Tenga en cuenta que los árboles construir y deconstruir (a través de coincidencia de patrones) con la misma sintaxis , que también es exactamente cómo se imprimen (menos espacios).

Y también se pueden usar con mapas hash o conjuntos, ya que tienen un hashCode válido y estable.

22

Nadie mencionó que las clases de casos son también ejemplos de Product y por lo tanto heredan estos métodos:

def productElement(n: Int): Any 
def productArity: Int 
def productIterator: Iterator[Any] 

donde el productArity devuelve el número de parámetros de clase productElement(i), devuelve el parámetro iº, y productIterator permite iterar a través de ellos.

+1

No son instancias de Product1, Product2, etc., sin embargo. –

23

Nadie mencionó que las clases de casos tienen val parámetros de constructor, pero este también es el predeterminado para las clases regulares (que I think is an inconsistency en el diseño de Scala). Darío dio a entender que tales como "inmutables".

Tenga en cuenta que puede anular el valor predeterminado anteponiendo el argumento de cada constructor con var para clases de casos. Sin embargo, al hacer que las clases de casos sean mutables, sus métodos equals y hashCode son variantes de tiempo. [ 1]

sepp2k ya se ha mencionado que las clases de casos generan automáticamente equals y hashCode métodos.

Además, nadie mencionó que las clases de casos crean automáticamente un compañero object con el mismo nombre que la clase, que contiene los métodos apply y unapply. El método apply permite construir instancias sin preceder al new. El método del extractor unapply permite la coincidencia de patrones que otros mencionaron.

También el compilador optimiza la velocidad de match - case coincidencia de patrón para las clases de casos [ 2].

[ 1] Case Classes Are Cool

[ 2] Case Classes and Extractors, pg 15.

9

La construcción de la clase de la caja en Scala también se puede ver como una conveniencia para eliminar algunas repeticiones.

Al construir una clase de caso, Scala le ofrece lo siguiente.

  • Se crea una clase, así como su objeto acompañante
  • Su objeto acompañante implementa el método apply que son capaces de utilizar como un método de fábrica. Obtiene la ventaja sintáctica del azúcar de no tener que usar la palabra clave nueva.

Debido a que la clase es inmutable que presentamos lo mejor descriptores de acceso, que son sólo las variables (o propiedades) de la clase, pero no mutadores (lo que no hay posibilidad de cambiar las variables). Los parámetros del constructor están automáticamente disponibles para usted como campos públicos de solo lectura. Mucho mejor para usar que la construcción de Java Bean.

  • Usted también consigue hashCode, equals, y toString métodos por defecto y el método equals compara un objeto estructuralmente. Se genera un método copy para poder clonar un objeto.

La mayor ventaja que se ha mencionado anteriormente es el hecho de que puede coincidir con el patrón en las clases de casos. La razón de esto es porque obtienes el método unapply que te permite deconstruir una clase de caso para extraer sus campos.


En esencia, lo que está recibiendo de Scala al crear una clase de caso (o un objeto caso si la clase no tiene argumentos) es un objeto único que sirve al propósito como una fábricay como extractor .

+0

¿Por qué necesitarías una copia de un objeto inmutable? –

3

Clase:

scala> class Animal(name:String) 
defined class Animal 

scala> val an1 = new Animal("Padddington") 
an1: Animal = [email protected] 

scala> an1.name 
<console>:14: error: value name is not a member of Animal 
     an1.name 
     ^

Pero si usamos mismo código, pero caso de uso de clases: Clase

scala> case class Animal(name:String) 
defined class Animal 

scala> val an2 = new Animal("Paddington") 
an2: Animal = Animal(Paddington) 

scala> an2.name 
res12: String = Paddington 


scala> an2 == Animal("fred") 
res14: Boolean = false 

scala> an2 == Animal("Paddington") 
res15: Boolean = true 

Persona:

scala> case class Person(first:String,last:String,age:Int) 
defined class Person 

scala> val harry = new Person("Harry","Potter",30) 
harry: Person = Person(Harry,Potter,30) 

scala> harry 
res16: Person = Person(Harry,Potter,30) 
scala> harry.first = "Saily" 
<console>:14: error: reassignment to val 
     harry.first = "Saily" 
       ^
scala>val saily = harry.copy(first="Saily") 
res17: Person = Person(Saily,Potter,30) 

scala> harry.copy(age = harry.age+1) 
res18: Person = Person(Harry,Potter,31) 

coincidencia de patrones:

scala> harry match { 
    | case Person("Harry",_,age) => println(age) 
    | case _ => println("no match") 
    | } 
30 

scala> res17 match { 
    | case Person("Harry",_,age) => println(age) 
    | case _ => println("no match") 
    | } 
no match 

objeto: Singleton:

scala> case class Person(first :String,last:String,age:Int) 
defined class Person 

scala> object Fred extends Person("Fred","Jones",22) 
defined object Fred 
2

Nadie ha mencionado ese objeto compañera de clase caso tiene tupled defention, que tiene un tipo:

case class Person(name: String, age: Int) 
//Person.tupled is def tupled: ((String, Int)) => Person 

El único caso de uso que puedo encontrar es cuando se necesita para construir clase de caso de tuple, ejemplo:

val bobAsTuple = ("bob", 14) 
val bob = (Person.apply _).tupled(bobAsTuple) //bob: Person = Person(bob,14) 

Puede hacer lo mismo, sin tupla, creando objetos directamente, pero si sus conjuntos de datos expresados ​​como una lista de tuplas con arity 20 (tupla con 20 elementos), pueden estar usando tupled es su elección.

4

Según documentation de Scala:

clases de casos son las clases simplemente regulares que son:

  • inmutable por defecto
  • descomponible a través pattern matching
  • Comparado por la igualdad estructural en lugar de por referencia
  • Sucinta para instanciar y operar en

Otra característica de la caso palabra clave es el compilador genera automáticamente varios métodos para nosotros, incluyendo el método toString familiar, es igual, y los métodos hashCode en Java.

2

A clase de caso es una clase que se puede utilizar con la instrucción match/case.

def isIdentityFun(term: Term): Boolean = term match { 
    case Fun(x, Var(y)) if x == y => true 
    case _ => false 
} 

ves que case es seguido por una instancia de la clase Diversión cuyo parámetro es una segunda Var. Esta es una sintaxis muy buena y poderosa, pero no puede funcionar con instancias de ninguna clase, por lo tanto, hay algunas restricciones para las clases de casos. Y si se obedecen estas restricciones, es posible definir automáticamente hashcode y equals.

La frase vaga "un mecanismo recursivo de descomposición mediante la coincidencia de patrones" significa simplemente "funciona con case". (De hecho, la instancia seguida de match se compara con (emparejado en contra) la instancia que sigue case, Scala tiene que descomponer los dos, y tiene a descomponerse de forma recursiva lo que están hechos.)

clases Qué caso son ¿útil para? El Wikipedia article about Algebraic Data Types ofrece dos buenos ejemplos clásicos, listas y árboles. El soporte para tipos de datos algebraicos (incluido saber cómo compararlos) es imprescindible para cualquier lenguaje funcional moderno.

¿Qué clases de casos son no útil para? Algunos objetos tienen estado, el código como connection.setConnectTimeout(connectTimeout) no es para clases de casos.

Y ahora se puede leer A Tour of Scala: Case Classes

0
  • clases de casos definen un objeto compagnon con aplicar y métodos anular la aplicación
  • clases de casos se extiende Serializable
  • clases de casos definen igual hashCode y copiar los métodos
  • Todos los atributos del constructor son val (azúcar sintáctico)
1

A diferencia de las clases, las clases de casos solo se usan para guardar datos.

Las clases de casos son flexibles para aplicaciones centradas en datos, lo que significa que puede definir campos de datos en la clase de caso y definir la lógica de negocios en un objeto complementario.De esta forma, está separando los datos de la lógica comercial.

Con el método de copia, puede heredar cualquiera o todas las propiedades requeridas de la fuente y puede cambiarlas como desee.

1

Aparte de lo que la gente ya se ha dicho, hay algunas diferencias básicas entre más class y case class

1. Case Class no necesita explícita new, mientras que la clase tiene que ser llamado con new

val classInst = new MyClass(...) // For classes 
val classInst = MyClass(..)  // For case class 

2.Por los parámetros de los constructores por defecto son privados en class, mientras que su público en case class

// For class 
class MyClass(x:Int) { } 
val classInst = new MyClass(10) 

classInst.x // FAILURE : can't access 

// For caseClass 
case class MyClass(x:Int) { } 
val classInst = MyClass(10) 

classInst.x // SUCCESS 

3. case class se comparan por valor

// case Class 
class MyClass(x:Int) { } 

val classInst = new MyClass(10) 
val classInst2 = new MyClass(10) 

classInst == classInst2 // FALSE 

// For Case Class 
case class MyClass(x:Int) { } 

val classInst = MyClass(10) 
val classInst2 = MyClass(10) 

classInst == classInst2 // TRUE 
Cuestiones relacionadas