2010-02-22 11 views
360

Я искал в Google, чтобы найти различия между case class и class. Все упоминают, что, когда вы хотите выполнить сопоставление образцов в классе, используйте класс case. В противном случае используйте классы, а также упомяните о некоторых дополнительных привилегиях, таких как equals и hash code overriding. Но являются ли они единственными причинами, почему следует использовать класс case вместо класса?В чем разница между классом и классом класса Scala?

Я предполагаю, что в Scala должна быть какая-то очень важная причина. Каково объяснение или есть ли ресурс, чтобы узнать больше о классах событий Scala?

ответ

318

Примеры классов можно рассматривать как простые и неизменные объекты хранения данных, которые должны исключительно зависеть от их аргументов конструктора.

Эта функциональная концепция позволяет

  • использовать компактные инициализации синтаксис (Node(1, Leaf(2), None)))
  • разлагать их с помощью шаблона
  • имеет сравнение равенства неявно определенный

В сочетании с наследованием , классы case используются для имитации algebraic datatypes.

Если объект выполняет вычисления с учетом состояния внутри или проявляет другие виды сложного поведения, он должен быть обычным классом.

+0

@Dario Спасибо за указателями. Итак, ADT - это что-то вроде Enums? –

+9

@Teja: В некотором роде. ADT являются параметрами enum *, чрезвычайно мощными и типичными. – Dario

+7

Классы закрытых классов используются для имитации алгебраических типов данных. В противном случае количество подклассов не ограничено. –

50
  • Case классы могут быть шаблон соответствует
  • классы Case автоматически определяют хэш и равен
  • Примеры классов автоматически определять методы геттер для аргументов конструктора.

(Вы уже упоминали все, кроме последнего).

Это единственные отличия от обычных классов.

+10

Сетчаты не генерируются для классов case, если в аргументе конструктора не указано «var», и в этом случае вы получаете одинаковое генерация/сборщик в качестве обычных классов. –

+1

@Mitch: Верно, мое плохое. Исправлено. – sepp2k

+0

Вы опустили 2 отличия, см. Мой ответ. –

144

Технически, нет разницы между классом и классом case, даже если компилятор оптимизирует некоторые вещи при использовании классов классов. Однако класс случая используется для устранения плиты котла для определенного рисунка, который реализует algebraic data types.

Очень простой пример таких типов - это деревья. Бинарное дерево, например, может быть реализована следующим образом:

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 

Это позволит нам сделать следующее:

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

Обратите внимание, что деревья строительства и деконструкции (через сопоставление с образцом) с тем же синтаксисом , что также точно так, как они печатаются (минус пробелы).

И они также могут использоваться с картами или наборами хэшей, так как они имеют действительный стабильный хэш-код.

22

Никто не упомянул, что классы случаев являются также случаями Product и, таким образом, наследуют эти методы:

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

где productArity возвращает число параметров класса, productElement(i) возвращает яя параметр и productIterator позволяет выполнять итерацию через них.

+1

Однако они не являются экземплярами Product1, Product2 и т. Д. –

23

Никто не упоминает, что классы классов имеют параметры конструктора val, но это также значение по умолчанию для обычных классов (которое I think is an inconsistency в дизайне Scala). Дарио предположил, что там, где он отметил, что они «неизменный».

Обратите внимание, что вы можете переопределить значение по умолчанию, добавив аргумент каждого конструктора к var для классов case. Тем не менее, изменение класса case приводит к тому, что их методы equals и hashCode являются вариантами времени. [ 1]

sepp2k уже упоминалось, что классы случае автоматически генерировать equals и hashCode методы.

Также никто не упоминает, что классы классов автоматически создают компаньона object с тем же именем, что и класс, который содержит и unapply методов. Метод apply позволяет создавать экземпляры без добавления с new. Метод экстракции unapply позволяет сопоставить образцы, упомянутые другими.

Также компилятор оптимизирует скорость match - case соответствие шаблону для классов корпусов [ 2].

[ 1] Case Classes Are Cool

[ 2] Case Classes and Extractors, pg 15.

9

Конструкция класса корпуса в Scala также может рассматриваться как удобство для удаления некоторых шаблонов.

При построении класса корпуса Scala дает вам следующее.

  • Это создает класс, а также его компаньон объект
  • Его объект компаньон реализует apply метод, который вы можете использовать в качестве фабричного метода. Преимущество синтаксического сахара в том, что вам не нужно использовать новое ключевое слово.

Поскольку класс неизменен вы получаете аксессоров, которые только переменные (или свойства) класса, но не мутаторов (так что нет возможности изменить переменные). Параметры конструктора автоматически доступны для вас в качестве общедоступных полей. Намного лучше использовать, чем Java bean-конструкцию.

  • Вы также получаете hashCode, equals и toString методы по умолчанию и метод equals сравнивает объект структурно. Создается способ copy, позволяющий клонировать объект.

Наибольшее преимущество, как уже упоминалось ранее, заключается в том, что вы можете сопоставлять образцы по классам случаев. Причина этого заключается в том, что вы получаете метод unapply, который позволяет вам деконструировать класс case для извлечения его полей.


В сущности, что вы получаете от Scala при создании тематического класса (или объекта случая, если ваш класс не имеет аргументов) является объектом синглтона, который служит цели в качестве завода и как экстрактора.

+0

Зачем вам нужна копия неизменяемого объекта? –

3

Класс:

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 
     ^

Но если мы будем использовать тот же код, но прецедентов класс:

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 

Person класс:

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) 

Pattern Matching:

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 

объект: синглтон:

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

Никто не говорил, что объект случае класс компаньона tupled defention, который имеет тип:

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

Единственный случай использования я могу найти, когда вам нужно построить класс случая из кортежа, пример:

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

Вы можете сделать то же самое, без пополнения, путем создания объекта напрямую, но если ваши наборы данных, выраженные в виде списка кортежей с arity 20 (кортеж с 20 элементами), может быть использован по вашему выбору.

4

Согласно documentation в Scala:

классов Case только регулярные занятия, которые:

  • Неизменных по умолчания
  • разложимого через pattern matching
  • По сравнению со структурным равенством, а не по ссылке
  • Краткая информация о создании и работать на

Еще одна особенность случае ключевого слова компилятор автоматически генерирует несколько методов для нас, в том числе знакомый ToString, равных и методы Hashcode в Java.

2

A класс дела - класс, который может использоваться с заявлением match/case.

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

Вы видите, что case следует экземпляр класса Fun, чей второй параметр является Var. Это очень хороший и мощный синтаксис, но он не может работать с экземплярами какого-либо класса, поэтому есть некоторые ограничения для классов case. И если эти ограничения соблюдаются, можно автоматически определить hashcode и equals.

Неясная фраза «рекурсивный механизм разложения посредством сопоставления с образцом» означает просто «работает с case». (Действительно, экземпляр с последующим match сравнивается (сопоставляется) экземпляр, который следует case, Scala имеет разлагать их оба, и имеет рекурсивно разлагается, что они сделаны.)

Что тематических классов являются полезно для? Wikipedia article about Algebraic Data Types дает два хороших классических примера, списки и деревья. Поддержка алгебраических типов данных (включая знание того, как их сравнивать) является обязательным для любого современного функционального языка.

классы корпусане полезный для? Некоторые объекты имеют состояние, код connection.setConnectTimeout(connectTimeout) не для классов case.

И теперь вы можете читать A Tour of Scala: Case Classes

0
  • классы Примеры определения объекта Компаньон с применить и исключить его методы
  • классы Case расширяет Сериализуемые
  • классы Case определяют равен хэш-код и скопировать Методы
  • Все атрибуты конструктора являются вал (синтаксический сахар)
1

В отличие от классов классы case используются только для хранения данных.

Примеры классов гибки для приложений, ориентированных на данные, что означает, что вы можете определять поля данных в классе case и определять бизнес-логику в сопутствующем объекте.Таким образом, вы отделяете данные от бизнес-логики.

С помощью метода копирования вы можете наследовать любые или все необходимые свойства из источника и изменять их по своему усмотрению.

1

Помимо того, что люди уже сказали, есть еще некоторые основные различия между class и case class

1. Case Class не нуждается в явном new, в то время как класс должны быть вызваны с new

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

2.By Параметры конструктора по умолчанию указаны в class, а его публикация находится в 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 сравнивают себя по значению

// 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 
Смежные вопросы