案例对象和对象之间的区别
scala 中的 case 对象和对象有什么区别吗?
Is there any difference between case object and object in scala?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
scala 中的 case 对象和对象有什么区别吗?
Is there any difference between case object and object in scala?
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
接受
或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
发布评论
评论(7)
这里有一个区别 - case 对象扩展了
Serialized
特征,因此它们可以被序列化。默认情况下,常规对象不能:Here's one difference - case objects extend the
Serializable
trait, so they can be serialized. Regular objects cannot by default:Case 类与常规类的不同之处在于它们具有:
equals
和hashCode
的默认实现toString
默认实现>,以及scala.Product
自动继承而获得的少量功能。模式匹配、equals 和 hashCode 对于单例来说并不重要(除非你做了一些真正退化的事情),所以你几乎只是得到序列化、一个漂亮的
toString
和一些你可能不会的方法。从来没有使用过。Case classes differ from regular classes in that they get:
equals
andhashCode
toString
, andscala.Product
.Pattern matching, equals and hashCode don't matter much for singletons (unless you do something really degenerate), so you're pretty much just getting serialization, a nice
toString
, and some methods you probably won't ever use.序列化差异:
toString 区别:
Serialization difference:
toString difference:
一个巨大的死灵,但这是谷歌官方教程之外这个问题的最高结果,一如既往,对细节相当模糊。下面是一些简单的对象:
现在,让我们在编译的 .class 文件上使用 IntelliJ 的“将 Scala 反编译为 Java”非常有用的功能:
如您所见,这是一个非常简单的单例模式,除了本问题范围之外的原因之外,生成两个类:静态
StandardObject
(如果对象定义任何静态转发器方法,它将包含静态转发器方法)和实际的单例实例StandardObject$
,其中代码中定义的所有方法最终都作为实例方法。当您实现Serialized
时,事情会变得更有趣:编译器不会将自己限制为简单地创建“实例”(非静态)类
Serialized
,它会添加一个>writeReplace
方法。writeReplace
是writeObject
/readObject
的替代方案;它的作用是,当具有此方法的Serialized
类被序列化时,它会序列化不同的对象。然后,在反序列化时,该代理对象的readResolve
方法在反序列化后就会被调用。这里,ModuleSerializedProxy
实例使用携带Class[SerializedObject]
的字段进行序列化,因此它知道需要解析什么对象。该类的readResolve
方法仅返回SerializedObject
- 因为它是具有无参数构造函数的单例,所以 scalaobject
在结构上始终等于其自身不同的VM实例和不同的运行,通过这种方式,每个VM实例仅创建该类的单个实例的属性被保留。值得注意的是,这里存在一个安全漏洞:SerializedObject$
中没有添加readObject
方法,这意味着攻击者可以恶意准备与标准 Java 序列化相匹配的二进制文件SerializedObject$
的格式和“单例”的单独实例将被创建。现在,让我们转向
case 对象
:还有更多的事情发生,因为
CaseObject$
现在还实现了Product0
及其迭代器和访问器方法。我不知道此功能的用例,它可能是为了与case class
保持一致,而 case class 始终是其字段的产品。这里主要的实际区别是我们免费获得canEqual
、hashCode
和toString
方法。仅当您决定将它与不是单例对象的Product0
实例进行比较时,canEqual
才相关,toString
使我们免于实现单个简单对象方法,当 case 对象用作枚举常量而不实现任何行为时,该方法非常有用。最后,正如人们可能怀疑的那样,hashCode
返回一个常量,因此它对于所有 VM 实例都是相同的。如果序列化一些有缺陷的哈希映射实现,这一点很重要,但标准 java 和 scala 哈希映射都会明智地在反序列化时重新哈希所有内容,所以这应该不重要。请注意,equals
未被覆盖,因此它仍然是引用相等,并且安全漏洞仍然存在。这里有一个巨大的警告:如果一个 case 对象从Object
之外的某个超类型继承equals
/toString
,则不会生成相应的方法,并且而是使用继承的定义。TL;DR:实践中唯一重要的区别是 toString 返回对象的非限定名称。
不过,我必须在这里声明:除了字节码中实际存在的内容之外,我不能保证编译器不会特别对待 case 对象。当模式匹配案例类时,除了它们实现
unapply
之外,它肯定会这样做。A huge necro, but it is the highest result for this question in Google outside official tutorial which, as always, is pretty vague about the details. Here are some bare bones objects:
Now, lets use the very useful feature of IntelliJ 'decompile Scala to Java' on compiled .class files:
As you can see, a pretty straightforward singleton pattern, except for reasons outside the scope of this question, two classes are generated: the static
StandardObject
(which would contain static forwarder methods should the object define any) and the actual singleton instanceStandardObject$
, where all methods defined in the code end up as instance methods. Things get more intresting when you implementSerializable
:The compiler doesn't limit itself to simply making the 'instance' (non-static) class
Serializable
, it adds awriteReplace
method.writeReplace
is an alternative towriteObject
/readObject
; what it does, it serializes a different object whenether theSerializable
class having this method is being serialized. On deserializention then, that proxy object'sreadResolve
method is invoked once it is deserialized. Here, aModuleSerializableProxy
instance is serialized with a field carrying theClass[SerializableObject]
, so it knows what object needs to be resolved. ThereadResolve
method of that class simply returnsSerializableObject
- as it is a singleton with a parameterless constructor, scalaobject
is always structurally equal to itself between diffrent VM instances and different runs and, in this way, the property that only a single instance of that class is created per one VM instance is preserved. A thing of note is that there is a security hole here: noreadObject
method is added toSerializableObject$
, meaning an attacker can maliciously prepare a binary file which matches standard Java serialization format forSerializableObject$
and a separate instance of the 'singleton' will be created.Now, lets move to the
case object
:A lot more is going on, as
CaseObject$
now implements alsoProduct0
, with its iterator and accessor methods. I am unaware of a use case for this feature, it is probably done for consistency withcase class
which is always a product of its fields. The main practical difference here is that we getcanEqual
,hashCode
andtoString
methods for free.canEqual
is relevant only if you decide to compare it with aProduct0
instance which is not a singleton object,toString
saves us from implementing a single simple method, which is useful when case objects are used as enumeration constants without any behaviour implemented. Finally, as one might suspect,hashCode
returns a constant, so it is the same for all VM instances. This matters if one serializes some flawed hash map implementation, but both standard java and scala hash maps wisely rehash all contents on deserialization, so it shouldn't matter. Note thatequals
is not overriden, so it is still reference equality, and that the security hole is still there. A huge caveat here: if a case object inheritequals
/toString
from some supertype other thanObject
, the corresponding methods are not generated, and the inherited definitions are used instead.TL;DR: the only difference that matters in practice is the
toString
returning the unqualified name of the object.I must make a disclamer here, though: I cannot guarantee that the compiler doesn't treat case objects specially in addition to what is actually in the bytecode. It certainly does so when patterm matching case classes, aside from them implementing
unapply
.与
case class
和class
类似,我们只是在没有情况下使用case object
而不是case class
表示附加状态信息的任何字段。It's similar with
case class
andclass
,we just usecase object
instead ofcase class
when there isn't any fields representing additional state information.case 对象隐式地带有 toString、equals 和 hashCode 方法的实现,但简单对象则不然。
case 对象可以序列化,而简单对象则不能,这使得 case 对象作为 Akka-Remote 的消息非常有用。
在 object 关键字之前添加 case 关键字使得对象可序列化。
case objects implicitly come with implementations of methods toString, equals, and hashCode, but simple objects don't.
case objects can be serialized while simple objects cannot, which makes case objects very useful as messages with Akka-Remote.
Adding the case keyword before object keyword makes the object serializable.
我们之前知道对象和“案例类”。但“案例对象”是两者的混合,即它是类似于对象的单例,并且具有大量样板,如案例类中一样。唯一的区别是样板是针对对象而不是类完成的。
case 对象不会附带以下方法:
Apply、Un-apply 方法。
这里没有复制方法,因为这是一个单例。
没有结构平等比较的方法。
也没有构造函数。
We know objects and "case class" before. But "case object" is a mix of both i.e it is a singleton similar to an object and with a lot of boilerplate as in a case class. The only difference is that the boilerplate is done for an object instead of a class.
case objects won't come with the below ones:
Apply, Un-apply methods.
here are no copy methods since this is a singleton.
No method for structural equality comparison.
No constructor as well.