В чем разница между классом case и классом Scala?
Я искал в Google, чтобы найти различия между case class
и class
. Все упоминают, что когда вы хотите сделать сопоставление шаблонов в классе, используйте класс case. В противном случае используйте классы, а также упоминание некоторых дополнительных льгот, таких как equals и переопределение хэш-кода. Но являются ли это единственными причинами, по которым следует использовать класс case вместо класса?
Я думаю, что должна быть какая-то очень важная причина для этой функции в Scala. Каково объяснение или есть ли ресурс, чтобы узнать больше о классах Scala case?
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].
конструкция класса 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.