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 对象的信息,并与模式中的名字、年龄和俱乐部进行匹配。

相关推荐
进击的雷神4 小时前
Perl语言深度考查:从文本处理到正则表达式的全面掌握
开发语言·后端·scala
进击的雷神5 小时前
Perl测试起步:从零到精通的完整指南
开发语言·后端·scala
旋风小飞棍3 天前
如何在sheel中运行spark
大数据·开发语言·scala
rylshe13143 天前
在scala中sparkSQL连接mysql并添加新数据
开发语言·mysql·scala
MZWeiei5 天前
Spark任务调度流程详解
大数据·分布式·spark·scala
бесплатно5 天前
Scala流程控制
开发语言·后端·scala
Bin Watson12 天前
解决 Builroot 系统编译 perl 编译报错问题
开发语言·scala·perl
什么芮.15 天前
大数据应用开发和项目实战(2)
大数据·pytorch·sql·spark·scala
不要天天开心17 天前
Spark-Streaming核心编程:有状态转化操作与DStream输出
scala
欧先生^_^18 天前
Scala语法基础
开发语言·后端·scala