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

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

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

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

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

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


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

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

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


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

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

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

также никто не упомянул, что классы case автоматически создают компаньона object С тем же именем, что и класс, который содержит apply и unapply методы. The apply метод позволяет создавать экземпляры без добавления new. The 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 companion имеет 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 - это класс, который может использоваться с match/case заявление.

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

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

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

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

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

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


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

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

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

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

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