8 scala的伴生对象

1 单例对象

在编写 Java 程序时,我们经常会通过编写静态方法代码,去封装常用的 Utility 类。

在 Scala 中没有静态成员这一概念,所以,如果我们要定义静态属性或方法,就需要使用 Scala 的单例对象 object。Scala 的对象跟 Javascript 中定义一个对象,概念是差不多的。

下面定义一个球员对象,并在 main 函数打印球员对象的相关属性:

scala 复制代码
/**
 * 球员对象
 */
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)
  }
}

2 工具类案例

我们可以利用单例对象实现工具类,例如,下面实现了一个简易的 DateUtils

scala 复制代码
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"))
  }
}

运行后,控制台打印:

markdown 复制代码
true
false
31536000

3 伴生对象

如果想一个类,既需要静态成员,又需要实例成员,在 Scala 中可以使用伴生对象(companion object)来实现。

3.1 伴生对象的定义

伴生对象有以下特点:

(1) 伴生对象 和 类 必须要在同一个 class 文件中。

(2) 伴生对象名字要和类名字一致。

(3) 伴生类 和 伴生对象可以互相访问彼此的 private 属性。

scala 复制代码
/**
 * 球员信息类
 */
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))
  }
}

3.2 apply 及 unapply 方法

在 Scala 中,applyunapply 是两个特殊的方法,它们通常与伴生对象一起使用,并且在模式匹配、构造对象等方面发挥着重要作用。

3.2.1 apply 方法

apply 方法通常用于对象的构造。当你调用类似 ClassName(args) 的代码时,实际上是调用了类的伴生对象的 apply 方法。这使得你可以像调用函数一样构造对象,而不需要显式地使用 new 关键字。

例如,我们在定义一个列表时,并不需要使用 new: val list = List(1, 2, 3),下面为球员信息类的共生对象定义了 apply 方法:

scala 复制代码
/**
 * 球员信息类
 */
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))
  }
}

3.2.2 unapply 方法

unapply 方法通常用于模式匹配。它是 Extractor 模式的一部分,允许你从对象中提取部分信息,并将其与模式进行匹配。

例如:

scala 复制代码
/**
 * 球员信息类
 */
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 对象的信息,并与模式中的名字、年龄和俱乐部进行匹配。

相关推荐
宅小海9 小时前
scala String
大数据·开发语言·scala
懒惰的橘猫9 小时前
Scala的迭代器操作实例
scala
锅包肉的九珍9 小时前
Scala的Array数组
开发语言·后端·scala
别惊鹊9 小时前
Scala案例:全文单词统计
scala
心仪悦悦9 小时前
Scala的Array(2)
开发语言·后端·scala
直裾10 小时前
Scala全文单词统计
开发语言·c#·scala
心仪悦悦10 小时前
Scala中的集合复习(1)
开发语言·后端·scala
西域编娃1 天前
Scala 编程实战:梦想清单管理器
开发语言·后端·scala