1. 概述

在本篇文章中,我们将深入探讨 Scala 中的 包(Package)导入(Import) 以及 包对象(Package Object) 的使用方式。

我们会看到不同形式的类型导入方法、包的定义方式,以及一些强大的包特性,这些特性在实际开发中非常实用。

2. Scala 中的导入机制

导入语句用于将所需的类或类型引入当前文件的作用域内。下面我们来了解几种常见的导入方式。

2.1. 基础导入

✅ 单个类的导入非常直观,和 Java 类似:

import com.baeldung.scala.packageimport.vehicle.Bicycle

如果要导入某个包下的所有类,可以使用通配符 _

import com.baeldung.scala.packageimport.vehicle._

也可以在一行中显式指定多个类进行导入:

import com.baeldung.scala.packageimport.vehicle.{Bicycle, Car, Vehicle}

2.2. 别名导入

有时候我们可能会遇到从不同包中导入同名类的情况,比如:

import java.util.Date
import java.sql.Date

这两个 Date 类返回的时间格式不同:

  • java.sql.Date 只显示日期
  • java.util.Date 显示日期和时间

为了避免命名冲突,我们可以使用别名导入:

import java.util.{Date => utilDate}
import java.sql.{Date => sqlDate}

之后就可以像普通变量一样使用它们了:

def run(): Unit = {
  val dt: utilDate = new utilDate()
  val dtSql: sqlDate = new sqlDate(System.currentTimeMillis())
  println(s"I am a vehicle running on $dt !")
  println(s"I am a vehicle running on $dtSql !")
}

输出示例:

I am a vehicle running on Wed May 27 13:50:53 CEST 2020 !
I am a vehicle running on 2020-05-27 !

2.3. 导入时隐藏特定类

有时候我们想导入一个包中的所有类,但排除其中某一个类。例如,只隐藏 java.util.Date

import java.util.{Date => _, _}
import java.sql.Date

这样我们就可以放心使用 java.sql.Date 而不用担心冲突:

override def run(): Unit = {
  super.run()
  val dtSql: Date = new Date(System.currentTimeMillis())
  println(s"$numberOfTires tires from the bicycle brand $brand are running on $dtSql")
}

3. Scala 中的包定义方式

Scala 提供了多种灵活的方式来定义包结构。

3.1. 简单包定义

和 Java 一样,在类文件顶部声明包名即可:

package com.baeldung.scala

package object packageimport {
  trait Motor {
    val dieselMessage: String = "I am not environment friendly"
    val noDieselMessage: String = "I am environment friendly"
  }
}

3.2. 在同一文件中定义多个包

通过大括号 {},我们可以在一个文件中定义多个包,并且每个包中可以有相同名称的类:

package com.baeldung.scala.packageimport.vehicle {
  import java.util.{Date => utilDate}
  import java.sql.{Date => sqlDate}

  abstract class Vehicle(numberOfTires: Int, brand: String) {

    def run(): Unit = {
      val dt: utilDate = new utilDate()
      val dtSql: sqlDate = new sqlDate(System.currentTimeMillis())
      println(s"I am a vehicle running on $dt !")
      println(s"I am a vehicle running on $dtSql !")
    }
  }
}

package com.baeldung.scala.packageimport.vehicleNew {
  import java.sql.{Date => sqlDate}

  abstract class Vehicle(numberOfTires: Int, brand: String) {

    def run(): Unit = {
      val dtSql: sqlDate = new sqlDate(System.currentTimeMillis())
      println(s"I am a NEW vehicle running on $dtSql !")
    }
  }
}

此时两个 Vehicle 类分别属于不同的包,可通过完整路径访问:

  • com.baeldung.scala.packageimport.vehicle.Vehicle
  • com.baeldung.scala.packageimport.vehicleNew.Vehicle

3.3. 包堆叠(Package Stacking)

我们可以连续写多个包声明,实现“包堆叠”效果。这相当于嵌套包的简写方式:

package com.baeldung.scala
package packageimport
package stacking

等价于:

package com.baeldung.scala.packageimport.stacking

⚠️ 但好处是:可以直接访问前面每一层包中的类,无需额外导入

也就是说,不需要再写 import com.baeldung.scala._ 就能直接使用该包下的类。

4. 包对象(Package Object)

✅ 包对象允许我们将方法、函数、变量等顶级成员绑定到某个包上,供该包的所有子成员共享。

每个包只能有一个包对象,通常命名为 package.scala,并放置在对应包目录下。

举个例子,为 packageimport 包创建一个包对象:

package com.baeldung.scala
package object packageimport {
  val year = "2020"
  trait Motor {
    val dieselMessage: String = "I am not environment friendly"
    val noDieselMessage: String = "I am environment friendly"
  }
}

在这个包下的所有类都可以直接访问 yearMotor无需显式导入包对象

object bmwB38 extends Motor {
 
  def run(): Unit =
    println(s"I am a bmwB38 ! $noDieselMessage")
}

5. 总结

本文我们系统地讲解了 Scala 中的导入语法、包的定义方式,以及包对象的强大功能。

主要收获包括:

✅ 不同的导入方式及其适用场景
✅ 如何处理同名类冲突问题
✅ 多种包定义方式及各自特点
✅ 包对象如何简化顶级成员共享

最后,别忘了代码都在 GitHub 上:https://github.com/Baeldung/scala-tutorials/tree/master/scala-lang-modules/scala-lang



原始标题:Packaging, Importing and Package Objects in Scala

« 上一篇: Scala 运算符入门
» 下一篇: 隐式类