在编写 Java 程序时,我们经常会通过编写静态方法代码,去封装常用的 Utility 类。
在 Scala 中没有静态成员这一概念,所以,如果我们要定义静态属性或方法,就需要使用 Scala 的单例对象 object。Scala 的对象跟 Javascript 中定义一个对象,概念是差不多的。
下面定义一个球员对象,并在 main
函数打印球员对象的相关属性:
/**
* 球员对象
*/
object FootballPlayerObject {
/**
* 姓名
*/
var NAME: String = "Mohamed Salah"
/**
* 年纪
*/
var AGE: Int = 31
/**
* 所在俱乐部
*/
var CLUB: String = "Liverpool"
/**
* 定义入口 main 函数,打印球员对象相关属性
* @param args
*/
def main(args: Array[String]): Unit = {
System.out.println(FootballPlayerObject.NAME)
System.out.println(FootballPlayerObject.AGE)
System.out.println(FootballPlayerObject.CLUB)
}
}
我们可以利用单例对象实现工具类,例如,下面实现了一个简易的 DateUtils
import org.joda.time.format.DateTimeFormat
/**
* 日期时间工具类
*/
object DateUtils {
val TIME_FORMAT = DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")
/**
* 判断一个时间是否在另一个时间之前
*
* @param time1 第一个时间
* @param time2 第二个时间
* @return 判断结果
*/
def before(time1: String, time2: String): Boolean = {
TIME_FORMAT.parseDateTime(time1).isBefore(TIME_FORMAT.parseDateTime(time2))
}
/**
* 判断一个时间是否在另一个时间之后
*
* @param time1 第一个时间
* @param time2 第二个时间
* @return 判断结果
*/
def after(time1: String, time2: String): Boolean = {
TIME_FORMAT.parseDateTime(time1).isAfter(TIME_FORMAT.parseDateTime(time2))
}
/**
* 计算时间差值(单位为秒)
*
* @param time1 时间1
* @param time2 时间2
* @return 差值
*/
def minus(time1: String, time2: String): Int = {
((TIME_FORMAT.parseDateTime(time1).getMillis - TIME_FORMAT.parseDateTime(time2).getMillis) / 1000).toInt
}
def main(args: Array[String]): Unit = {
println(DateUtils.before("2023-01-01 00:00:00", "2024-01-01 00:00:00"))
println(DateUtils.after("2023-01-01 00:00:00", "2024-01-01 00:00:00"))
println(DateUtils.minus("2024-01-01 00:00:00", "2023-01-01 00:00:00"))
}
}
运行后,控制台打印:
true
false
31536000
如果想一个类,既需要静态成员,又需要实例成员,在 Scala 中可以使用伴生对象(companion object
)来实现。
伴生对象有以下特点:
(1) 伴生对象 和 类 必须要在同一个 class 文件中。
(2) 伴生对象名字要和类名字一致。
(3) 伴生类 和 伴生对象可以互相访问彼此的 private
属性。
/**
* 球员信息类
*/
class PlayerInfo(private var playerName: String, var age: Int, var club: String) {
def hello(): String = {
s"Hey buddy, I am ${this.playerName} of ${this.club}, ${this.age} years old!"
}
}
/**
* PlayerInfo 类的共生对象
*/
object PlayerInfo {
/**
* 定义球员梦想
*/
private var dream: String = "The dream of %s is achieving World Cup"
/**
* 打印球员梦想
*/
def myDream(playerName: String): String = {
String.format(this.dream, playerName)
}
/**
* main 方法
* @param args
*/
def main(args: Array[String]): Unit = {
// 定义球员信息对象
val player: PlayerInfo = new PlayerInfo("Erling Haaland", 23, "Manchester City F.C.")
println(player.hello())
// 执行共生对象的 myDream 方法
// 可以访问共生类的私有 playerName
println(this.myDream(player.playerName))
}
}
在 Scala 中,apply
和 unapply
是两个特殊的方法,它们通常与伴生对象一起使用,并且在模式匹配、构造对象等方面发挥着重要作用。
apply
方法通常用于对象的构造。当你调用类似 ClassName(args) 的代码时,实际上是调用了类的伴生对象的 apply
方法。这使得你可以像调用函数一样构造对象,而不需要显式地使用 new 关键字。
例如,我们在定义一个列表时,并不需要使用 new
: val list = List(1, 2, 3)
,下面为球员信息类的共生对象定义了 apply
方法:
/**
* 球员信息类
*/
class PlayerInfo(private var playerName: String, var age: Int, var club: String) {
def hello(): String = {
s"Hey buddy, I am ${this.playerName} of ${this.club}, ${this.age} years old!"
}
}
/**
* PlayerInfo 类的共生对象
*/
object PlayerInfo {
/**
* 定义球员梦想
*/
private var dream: String = "The dream of %s is achieving World Cup"
/**
* 打印球员梦想
*/
def myDream(playerName: String): String = {
String.format(this.dream, playerName)
}
/**
* 定义 apply 方法,新建一个 PlayerInfo 对象
*
* @param playerName 球员名称
* @param age 年龄
* @return {@link PlayerInfo} 对象
*/
def apply(playerName: String, age: Int): PlayerInfo = new PlayerInfo(playerName, age, "Manchester City F.C.")
/**
* main 方法
* @param args
*/
def main(args: Array[String]): Unit = {
// 定义球员信息对象,有了 apply 方法后,不再需要 new 关键字
val player: PlayerInfo = PlayerInfo("Erling Haaland", 23)
println(player.hello())
// 执行共生对象的 myDream 方法
// 可以访问共生类的私有 playerName
println(this.myDream(player.playerName))
}
}
unapply
方法通常用于模式匹配。它是 Extractor
模式的一部分,允许你从对象中提取部分信息,并将其与模式进行匹配。
例如:
/**
* 球员信息类
*/
class PlayerInfo(private var playerName: String, var age: Int, var club: String) {
def hello(): String = {
s"Hey buddy, I am ${this.playerName} of ${this.club}, ${this.age} years old!"
}
}
/**
* PlayerInfo 类的共生对象
*/
object PlayerInfo {
/**
* 定义 apply 方法,新建一个 PlayerInfo 对象
*
* @param playerName 球员名称
* @param age 年龄
* @return {@link PlayerInfo} 对象
*/
def apply(playerName: String, age: Int): PlayerInfo = new PlayerInfo(playerName, age, "Manchester City F.C.")
/**
* 定义 unapply,作为提取器,提取球员 姓名,年龄,俱乐部
* @param playerInfo 球员信息对象
* @return
*/
def unapply(playerInfo: PlayerInfo): Option[(String, Int, String)] = Some(playerInfo.playerName, playerInfo.age, playerInfo.club)
/**
* main 方法
* @param args
*/
def main(args: Array[String]): Unit = {
// 定义球员信息对象,有了 apply 方法后,不再需要 new 关键字
val player: PlayerInfo = PlayerInfo("Erling Haaland", 23)
player match {
case PlayerInfo(name, age, club) => println(s"name: ${name}, age: ${age}, club: ${club}")
case _ => println("Not matched")
}
}
}
在上面的代码中,unapply
方法从 PlayerInfo
对象中提取了名字、年龄和俱乐部,并将它们作为元组返回。在 match
表达式中,case PlayerInfo(name, age, club)
部分使用了模式匹配,它调用了 PlayerInfo
伴生对象的 unapply
方法来提取 PlayerInfo
对象的信息,并与模式中的名字、年龄和俱乐部进行匹配。