Scala的case class和class有什么区别?
我在 Google 中搜索了 case class
和 class
之间的差异。每个人都提到,当你想在类上进行模式匹配时,请使用用例类。否则使用类并提及一些额外的好处,例如等于和哈希码覆盖。但是这些是人们应该使用案例类而不是类的唯一原因吗?
我想Scala中这个特性应该有一些非常重要的原因。解释是什么?或者是否有资源可以了解有关 Scala 案例类的更多信息?
I searched in Google to find the differences between a case class
and a class
. Everyone mentions that when you want to do pattern matching on the class, use case class. Otherwise use classes and also mentioning some extra perks like equals and hash code overriding. But are these the only reasons why one should use a case class instead of class?
I guess there should be some very important reason for this feature in Scala. What is the explanation or is there a resource to learn more about the Scala case classes from?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(17)
从技术上讲,类和案例类之间没有区别——即使编译器在使用案例类时确实优化了一些东西。但是,案例类用于消除特定模式的样板,该模式正在实现代数数据类型< /a>.
这种类型的一个非常简单的例子是树。例如,二叉树可以这样实现:
这使我们能够执行以下操作:
请注意,树使用相同的语法构造和解构(通过模式匹配),这也正是它们的打印方式(减去空格)。
它们还可以与哈希映射或集合一起使用,因为它们具有有效、稳定的哈希码。
Technically, there is no difference between a class and a case class -- even if the compiler does optimize some stuff when using case classes. However, a case class is used to do away with boiler plate for a specific pattern, which is implementing algebraic data types.
A very simple example of such types are trees. A binary tree, for instance, can be implemented like this:
That enable us to do the following:
Note that trees construct and deconstruct (through pattern match) with the same syntax, which is also exactly how they are printed (minus spaces).
And they can also be used with hash maps or sets, since they have a valid, stable hashCode.
(除了最后一个之外,您已经提到了所有内容)。
这些是与普通课程的唯一区别。
(You already mentioned all but the last one).
Those are the only differences to regular classes.
没有人提到案例类具有
val
构造函数参数,但这也是常规类的默认设置(我认为这是 Scala 设计中的不一致)。达里奥暗示了这一点,他指出它们是“不可变的”。请注意,您可以通过在案例类的每个构造函数参数前面添加
var
来覆盖默认值。但是,使 case 类可变会导致它们的equals
和hashCode
方法随时间变化。[1]sepp2k 已经提到案例类会自动生成
equals
和hashCode
方法。也没有人提到案例类会自动创建一个与类同名的伴随
对象
,其中包含apply
和unapply
方法。apply
方法允许构造实例,而无需在前面添加new
。unapply
提取器方法启用其他人提到的模式匹配。此外,编译器还优化了 case 类的
match
-case
模式匹配的速度[2]。[1] 案例类很酷
[2] 案例类和提取器,第 15 页。
No one mentioned that case classes have
val
constructor parameters yet this is also the default for regular classes (which I think is an inconsistency in the design of Scala). Dario implied such where he noted they are "immutable".Note you can override the default by prepending the each constructor argument with
var
for case classes. However, making case classes mutable causes theirequals
andhashCode
methods to be time variant.[1]sepp2k already mentioned that case classes automatically generate
equals
andhashCode
methods.Also no one mentioned that case classes automatically create a companion
object
with the same name as the class, which containsapply
andunapply
methods. Theapply
method enables constructing instances without prepending withnew
. Theunapply
extractor method enables the pattern matching that others mentioned.Also the compiler optimizes the speed of
match
-case
pattern matching for case classes[2].[1] Case Classes Are Cool
[2] Case Classes and Extractors, pg 15.
没有人提到案例类也是
Product
的实例,因此继承了这些方法:其中
productArity
返回类参数的数量,productElement(i) 返回 ith 参数,而
productIterator
允许迭代它们。No one mentioned that case classes are also instances of
Product
and thus inherit these methods:where the
productArity
returns the number of class parameters,productElement(i)
returns the ith parameter, andproductIterator
allows iterating through them.Scala 中的案例类构造也可以被视为删除一些样板文件的便利。
当构建案例类时,Scala 会为您提供以下内容。
apply
方法。您可以获得不必使用 new 关键字的语法糖优势。因为类是不可变的,所以您可以获得访问器,它们只是类的变量(或属性),但没有修改器(因此无法更改变量)。构造函数参数将作为公共只读字段自动提供给您。比 Java bean 构造好用得多。
hashCode
、equals
和toString
方法,并且equals
方法从结构上比较对象。生成复制方法以能够克隆对象(某些字段具有提供给该方法的新值)。前面提到的最大优点是您可以对案例类进行模式匹配。这样做的原因是因为您获得了
unapply
方法,该方法允许您解构案例类以提取其字段。本质上,在创建案例类(或者如果您的类不带参数,则为案例对象)时,您从 Scala 获得的是一个单例对象,其用作工厂和提取器。
The case class construct in Scala can also be seen as a convenience to remove some boilerplate.
When constructing a case class Scala gives you the following.
apply
method that you are able to use as a factory method. You get the syntactic sugar advantage of not having to use the new keyword.Because the class is immutable you get accessors, which are just the variables (or properties) of the class but no mutators (so no ability to change the variables). The constructor parameters are automatically available to you as public read only fields. Much nicer to use than Java bean construct.
hashCode
,equals
, andtoString
methods by default and theequals
method compares an object structurally. Acopy
method is generated to be able to clone an object (with some fields having new values provided to the method).The biggest advantage as has been mentioned previously is the fact that you can pattern match on case classes. The reason for this is because you get the
unapply
method which lets you deconstruct a case class to extract its fields.In essence what you are getting from Scala when creating a case class (or a case object if your class takes no arguments) is a singleton object which serves the purpose as a factory and as an extractor .
除了人们已经说过的之外,
class
和case class
之间还有一些更基本的区别1.
Case Class
不需要显式 < code>new,而类需要使用new
调用2.默认情况下,构造函数参数在
class
中是私有的,而在case class 中是公共的
3.
case class
按值比较自身Apart from what people have already said, there are some more basic differences between
class
andcase class
1.
Case Class
doesn't need explicitnew
, while class need to be called withnew
2.By Default constructors parameters are private in
class
, while its public incase class
3.
case class
compare themselves by value为了最终了解什么是案例类:
让我们假设以下案例类定义:
然后在终端中执行以下操作:
Scala 2.12.8 将输出:
正如我们所见,Scala 编译器生成一个常规类
Foo
和伴生对象Foo
。让我们浏览一下编译后的类并评论我们得到的内容:
Foo
类的内部状态,不可变:scala.Product
特征:scala.Equals
特征,用于通过==
使案例类实例具有可比性:java.lang.Object.hashCode
以遵守 equals- hashcode 契约:Object Foo:
- 方法
apply
用于实例化,无需new
关键字:unupply
用于在模式匹配中使用案例类 Foo:scala.runtime.AbstractFunction2
为了实现这样的技巧:tupled
from object 返回一个函数,通过应用元组来创建一个新的 Foo 2 个元素。所以案例类只是语法糖。
To have the ultimate understanding of what is a case class:
let's assume the following case class definition:
and then do the following in the terminal:
Scala 2.12.8 will output:
As we can see Scala compiler produces a regular class
Foo
and companion-objectFoo
.Let's go through the compiled class and comment on what we have got:
Foo
class, immutable:scala.Product
trait:scala.Equals
trait for make case class instances comparable for equality by==
:java.lang.Object.hashCode
for obeying the equals-hashcode contract:java.lang.Object.toString
:new
keyword:Object Foo:
- method
apply
for instantiation withoutnew
keyword:unupply
for using case class Foo in pattern matching:scala.runtime.AbstractFunction2
for doing such trick:tupled
from object returns a funtion to create a new Foo by applying a tuple of 2 elements.So case class is just syntactic sugar.
根据 Scala 的文档:
case 关键字的另一个特点是编译器会自动为我们生成几个方法,包括我们熟悉的 toString、equals 和 hashCode 方法在爪哇。
According to Scala's documentation:
Another feature of the case keyword is the compiler automatically generates several methods for us, including the familiar toString, equals, and hashCode methods in Java.
类:
但是如果我们使用相同的代码但使用案例类:
人员类:
模式匹配:
对象:单例:
Class:
But if we use same code but use case class:
Person class:
Pattern Matching:
object: singleton:
没有人提到 case 类伴随对象有
tupled
防御,它有一个类型:我能找到的唯一用例是当你需要从 tuple 构造 case 类时,例如:
你可以做同样的事情,而不需要tupled,通过直接创建对象来表示,但如果您的数据集表示为元数为 20 的元组列表(具有 20 个元素的元组),则可能会选择使用 tupled。
Nobody mentioned that case class companion object has
tupled
defention, which has a type:The only use case I can find is when you need to construct case class from tuple, example:
You can do the same, without tupled, by creating object directly, but if your datasets expressed as list of tuple with arity 20(tuple with 20 elements), may be using tupled is your choise.
与类不同,案例类仅用于保存数据。
案例类对于以数据为中心的应用程序来说非常灵活,这意味着您可以在案例类中定义数据字段并在伴随对象中定义业务逻辑。通过这种方式,您可以将数据与业务逻辑分开。
使用复制方法,您可以从源继承任何或所有必需的属性,并可以根据需要更改它们。
Unlike classes, case classes are just used to hold data.
Case classes are flexible for data-centric applications, which means you can define data fields in case class and define business logic in a companion object. In this way, you are separating the data from the business logic.
With the copy method, you can inherit any or all required properties from the source and can change them as you like.
case 类是可以与 match/case 语句一起使用的类。
您会看到
case
后面跟着一个 Fun 类的实例,其第二个参数是 Var。这是一个非常好的和强大的语法,但它不能用于任何类的实例,因此对案例类有一些限制。如果遵守这些限制,就可以自动定义 hashcode 和 equals。模糊的短语“通过模式匹配的递归分解机制”意味着“它适用于
case
”。 (事实上,match
后面的实例与case
后面的实例进行比较(匹配),Scala 必须分解它们,并且必须递归地分解它们是什么组成。)案例类有什么用处? 有关代数数据类型的维基百科文章给出了两个很好的经典示例:列表和树。对代数数据类型的支持(包括知道如何比较它们)是任何现代函数语言都必须的。
哪些案例类没有有用?有些对象具有状态,像
connection.setConnectTimeout(connectTimeout)
这样的代码不适用于案例类。现在您可以阅读 Scala 之旅:案例类
A case class is a class that may be used with the
match/case
statement.You see that
case
is followed by an instance of class Fun whose 2nd parameter is a Var. This is a very nice and powerful syntax, but it cannot work with instances of any class, therefore there are some restrictions for case classes. And if these restrictions are obeyed, it is possible to automatically define hashcode and equals.The vague phrase "a recursive decomposition mechanism via pattern matching" means just "it works with
case
". (Indeed, the instance followed bymatch
is compared to (matched against) the instance that followscase
, Scala has to decompose them both, and has to recursively decompose what they are made of.)What case classes are useful for? The Wikipedia article about Algebraic Data Types gives two good classical examples, lists and trees. Support for algebraic data types (including knowing how to compare them) is a must for any modern functional language.
What case classes are not useful for? Some objects have state, the code like
connection.setConnectTimeout(connectTimeout)
is not for case classes.And now you can read A Tour of Scala: Case Classes
我认为总的来说,所有答案都给出了关于类和案例类的语义解释。
这可能非常相关,但是每个 scala 新手都应该知道创建案例类时会发生什么。我写了这个答案,它简要地解释了案例类。
每个程序员都应该知道,如果他们使用任何预构建的函数,那么他们编写的代码相对较少,这使他们能够编写最优化的代码,但权力伴随着巨大的责任。因此,使用预构建函数时要非常小心。
一些开发人员由于额外的 20 个方法而避免编写案例类,您可以通过反汇编类文件看到这些方法。
如果您想检查案例类中的所有方法,请参考此链接。
I think overall all the answers have given a semantic explanation about classes and case classes.
This could be very much relevant, but every newbie in scala should know what happens when you create a case class. I have written this answer, which explains case class in a nutshell.
Every programmer should know that if they are using any pre-built functions, then they are writing a comparatively less code, which is enabling them by giving the power to write most optimized code, but power comes with great responsibilities. So, use prebuilt functions with very cautions.
Some developers avoid writing case classes due to additional 20 methods, which you can see by disassembling class file.
Please refer this link if you want to check all the methods inside a case class.
前面的答案中没有提到的一个重要问题是身份。常规类的对象具有标识,因此即使两个对象的所有字段具有相同的值,它们仍然是不同的对象。然而,对于案例类实例,相等性纯粹是根据对象字段的值来定义的。
One important issue not mentioned in earlier answers is that of identity. Objects of regular classes have identity, so even if two objects have identical values for all their fields, they are still different objects. For case class instances however, equality is defined in purely terms of the values of the object's fields.
下面列出了
案例类
的一些关键功能案例new
关键字的案例类。scala fiddle 上的示例 scala 代码,取自 scala 文档。
https://scalafiddle.io/sf/34XEQyE/0
Some of the key features of
case classes
are listed belownew
keyword.Sample scala code on scala fiddle, taken from the scala docs.
https://scalafiddle.io/sf/34XEQyE/0
案例类可以被视为简单且不可变的数据保存对象,它们应该完全依赖于它们的构造函数参数。
这个功能概念允许我们
Node(1, Leaf(2), None))
)与继承相结合,案例类是用于模仿代数数据类型。
如果一个对象在内部执行有状态计算或表现出其他类型的复杂行为,那么它应该是一个普通的类。
Case classes can be seen as plain and immutable data-holding objects that should exclusively depend on their constructor arguments.
This functional concept allows us to
Node(1, Leaf(2), None))
)In combination with inheritance, case classes are used to mimic algebraic datatypes.
If an object performs stateful computations on the inside or exhibits other kinds of complex behaviour, it should be an ordinary class.