如果你从面向对象编程的角度看待它们,那么你所陈述的大多数事实基本上是正确的。但是我们还有更多东西需要探讨。
对象
在Scala中,对象可以被视为模块,从函数式编程的角度来看。它们确实用于聚合你所称之为“实用函数”的类似函数。但是它们还具有其他含义。
object
可以被视为单例对象,因为你可以拥有一个继承特定trait
或class
的object
。
trait Bird
object Duck extends Bird
你还有伴生对象的概念。它是一个具有与该类相关模块函数的对象,您甚至可以从类中引用private
成员。
class Dog {
def eat(food: Food) = Dog.preferredFoods.contains(food)
}
object Dog {
private val preferredFoods = List(Ribeye, DogFood, Banana)
def walk(dog: Dog) = ???
}
类
你对类的理解是正确的。它们与Java的概念密切相关。
Trait
在Scala中,将trait
视为abstract class
的一种方式。但请注意,你也可以在Scala中拥有一个abstract class
,其行为与Java中相同。那么它们的区别是什么呢?
正如评论中指出的那样,可以将多个trait
混合在一起。此外,如果一个trait
完全是抽象的,即所有方法都像Java中的抽象类一样都是抽象的,那么trait
也可以被看作是一个Java的interface
。实际上,如果你的目标是与Java进行交互操作,这是声明interface
的方式。
sealed trait
只是告诉编译器,除了与之同文件中的其他类或特质以外,你不会有任何其他继承该类的情况。这有助于模式匹配,就像case类一样。因此编译器能够发出警告,告诉您模式匹配是否穷尽。但是请注意,Scala还有enum
。
Case类
可以将case类与sealed trait
一起使用来进行模式匹配。但case class
更像是一个“值类”。“case
”使编译器生成大量样板代码,因此你不必自己写。
你可以拥有自动的“伴生对象”,因此你可以在没有new
关键字的情况下实例化该对象,并使用自动生成的apply
函数。
你还可以自动获得hashCode
、equals
、toString
和copy
实现。并且对于所有构造函数的参数,你都可以自动获得val
。
scala> case class Room(area: Int)
defined class Room
scala> var r = Room(16)
r: Room = Room(16)
scala> r.hashCode
res2: Int = 1313771839
scala> r == Room(16)
res3: Boolean = true
scala> r == Room(15)
res4: Boolean = false
scala> r.toString
res5: String = Room(16)
scala> r.area
res6: Int = 16
scala> case class Point(x: Int, y: Int)
defined class Point
scala> val p = Point(1, 1)
p: Point = Point(1,1)
scala> val p1 = p.copy(y = 0)
p1: Point = Point(1,0)