斯卡拉的案例class和class级有什么区别?
我在谷歌searchfind一个case class
和一个class
之间的差异。 大家都提到,当你想在类上进行模式匹配时,使用case类。 否则,使用类,也提到一些额外的津贴,如等于和哈希代码覆盖。 但是,这些是唯一一个应该使用案例类而不是类的原因吗?
我想在Scala中这个function应该有一些非常重要的原因。 什么是解释或有一个资源来了解更多关于斯卡拉案例类从?
Case类可以看作是纯粹的,不可变的数据保存对象,它们应该完全依赖于它们的构造函数参数 。
这个function概念使我们能够
- 使用紧凑的初始化语法(
Node(1, Leaf(2), None))
) - 使用模式匹配来分解它们
- 隐含地定义了相等的比较
结合inheritance,case类用于模仿代数数据types 。
如果一个对象在内部执行有状态的计算或展现其他复杂行为,那么它应该是一个普通的类。
从技术上讲,类和case类之间没有区别 – 即使编译器在使用case类时也会优化某些东西。 然而,一个case类被用来去除特定模式的锅炉板,这是实现代数数据types 。
这种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 => }
请注意,树使用相同的语法构造和解构(通过模式匹配),这也正是它们如何打印(减去空格)。
而且它们也可以用于哈希映射或集合,因为它们有一个有效的,稳定的hashCode。
- 案例类可以模式匹配
- 案例类自动定义hashcode和equals
- Case类自动为构造函数参数定义getter方法。
(你已经提到除最后一个之外的所有东西)。
这是常规课程的唯一区别。
没有人提到case类有val
构造函数参数,但这也是常规类的默认值( 我认为这是 Scaladevise中的不一致 )。 达里奥暗示,他在这里指出他们是“ 不变的 ”。
请注意,您可以通过将每个构造函数的参数前置var
个案类来覆盖默认值。 然而,使case类变为可变的,导致它们的equals
和hashCode
方法是时变的。
sepp2k已经提到case类会自动生成equals
和hashCode
方法。
也没有人提到case类会自动创build一个同名的同名object
,它包含了apply
和unapply
方法。 apply
方法可以在不添加new
情况下构build实例。 不适用的提取器方法可以使其他人提到的模式匹配。
另外,编译器优化案例类match
– case
模式匹配的速度[2]。
案例分类很酷
[2] 案例分类和提取器,第15页 。
没有人提到case类也是Product
实例,因此inheritance了这些方法:
def productElement(n: Int): Any def productArity: Int def productIterator: Iterator[Any]
productArity
返回类参数的数量, productElement(i)
返回第i 个参数, productIterator
允许遍历它们。
Scala中的case类构造也可以被看作是方便的去除一些样板。
在构build一个案例类时,Scala为您提供以下内容。
- 它创build一个类以及它的伴侣对象
- 它的伴侣对象实现了可以用作工厂方法的
apply
方法。 您不必使用新的关键字就可以获得语法糖的优势。
因为这个类是不可变的,所以你可以得到访问器,这些访问器只是类的variables(或者属性),而没有variables(所以没有改变variables的能力)。 构造函数参数作为公共只读字段自动提供给您。 比Java bean构造更好用。
- 您还可以默认获取
hashCode
,equals
和toString
方法,equals
方法从结构上比较对象。 生成的copy
方法可以克隆一个对象。
如前所述,最大的优点是可以在案例类中进行模式匹配。 原因是因为你可以使用unapply
方法来解构一个case类来提取它的字段。
从本质上讲,当创build一个case类(或者一个case类,如果你的类没有参数)的时候你从Scala中获得的是一个单独的对象,它可以作为一个工厂和一个提取器 。
类:
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 ^
但是,如果我们使用相同的代码,但使用案例类:
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
对象: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
根据Scala的文档 :
案例类只是常规类,它们是:
- 不可变的默认情况下
- 通过模式匹配可分解
- 通过结构平等而不是参照来比较
- 简单地实例化和操作
case关键字的另一个特点是编译器自动为我们生成了几个方法,包括Java中常见的toString,equals和hashCode方法。
case类是可以与match/case
语句一起使用的类。
def isIdentityFun(term: Term): Boolean = term match { case Fun(x, Var(y)) if x == y => true case _ => false }
你看到这个case
后面跟着一个Fun类的实例,其第二个参数是Var。 这是一个非常好的强大的语法,但是它不能和任何类的实例一起工作,所以case类有一些限制。 如果遵守这些限制,可以自动定义哈希码和等号。
模糊的短语“通过模式匹配的recursion分解机制”意思就是“它适用于case
”。 (事实上, match
后面的实例与后面的实例进行比较(匹配),Scala必须分解它们,并且必须recursion地分解它们。
哪些案例类别有用? 维基百科关于代数数据types的文章给出了两个很好的经典例子,列表和树。 支持代数数据types(包括知道如何比较它们)对于任何现代函数式语言都是必须的。
什么案例类 没有用? 有些对象具有状态,像connection.setConnectTimeout(connectTimeout)
这样的代码不适用于大小写类。
现在你可以阅读斯卡拉游览:案例分类
没有人提到case类伴侣对象具有tupled defention,它有一个types:
case class Person(name: String, age: Int) //Person.tupled is def tupled: ((String, Int)) => Person
我能find的唯一用例是当你需要从元组构造case class时,例如:
val bobAsTuple = ("bob", 14) val bob = (Person.apply _).tupled(bobAsTuple) //bob: Person = Person(bob,14)
你可以通过直接创build对象来做同样的事情,但是如果你的数据集表示为arity 20(带有20个元素的元组)的元组列表,那么你可以使用tupled。
- 案例类用apply和unapply方法定义了一个compagnon对象
- 案例类扩展了Serializable
- Case类定义equals hashCode和copy方法
- 构造函数的所有属性都是val(语法糖)
与类不同,个案类仅用于保存数据。
案例类对于以数据为中心的应用程序非常灵活,这意味着您可以在案例类中定义数据字段并在伴随对象中定义业务逻辑。 通过这种方式,您将数据从业务逻辑中分离出来。
使用复制方法,您可以从源代码inheritance任何或所有必需的属性,并可以随意更改它们。
除了人们已经说过的话之外, class
与case class
之间还有一些更基本的区别
1. Case Class
不需要显式的new
,而Class需要用new
来调用
val classInst = new MyClass(...) // For classes val classInst = MyClass(..) // For case class
2.默认的构造函数参数在class
是私有的,而在public 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