В чем разница между классом case и классом Scala?


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

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

14 379

14 ответов:

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

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

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

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

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

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

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

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

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

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

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

(вы уже упомянули все, кроме последней).

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

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

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

здесь productArity возвращает количество параметров класса, productElement(i) возвращает Яе и productIterator позволяет перебирать их.

никто не упоминал, что классы case имеют val параметры конструктора, но это также по умолчанию для обычных классов (которые Я думаю, это непоследовательность в дизайне Scala). Дарио подразумевал такое, где он отметил, что они"неизменяемые".

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

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

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

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

[1] Case Классы Прохладно

[2] Case-классы и экстракторы, стр. 15.

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

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

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

поскольку класс является неизменяемым, вы получаете методы доступа, которые являются только переменными (или свойствами) класса, но не мутаторами (поэтому нет возможности изменять переменные). Параметры конструктора автоматически доступны вам как открытые поля только для чтения. Гораздо приятнее использовать, чем Java bean construct.

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

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


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

согласно Scala документация:

Case-классы обычные классы, которые являются:

  • неизменяемый по умолчанию
  • Разложимых через шаблоны
  • сравнивается по структурному равенству, а не по ссылке
  • краткое создание экземпляра и работать на

еще одна особенность случае ключевое слово компилятор автоматически генерирует для нас несколько методов, включая знакомые методы toString, equals и hashCode в Java.

класс:

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

scala> val an1 = new Animal("Padddington")
an1: Animal = Animal@748860cc

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

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

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

человек класс:

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)

Сопоставление С Образцом:

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

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

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

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

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

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

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

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

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

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

что case classes полезны? Элемент статья в Википедии об алгебраических типах данных дает два хороших классических примера, списки и деревья. Поддержка алгебраические типы данных (в том числе знание того, как их сравнивать) являются обязательными для любого современного функционального языка.

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

и теперь вы можете читать экскурсия по Scala: Case Classes

помимо того, что люди уже сказали, есть еще некоторые основные различия между 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

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

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

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

  • Case-классы определите объект бужу тоже награжден этим орденом применения и методы отмены
  • Case classes расширяет сериализуемый
  • классы Case определяют равные хэш-код и методы копирования
  • все атрибуты конструктора val (синтаксический сахар)

Я думаю, что в целом все ответы дали семантическое объяснение о классах и классах case. Это может быть очень актуально, но каждый новичок в scala должен знать, что происходит при создании класса case. Я написал этой ответ, который объясняет класс case в двух словах.

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

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

пожалуйста обратитесь к этой ссылке, Если вы хотите проверить все методы внутри класса case.