1. Overview

In this tutorial, we’ll discuss the concepts of packaging, importing, and package objects in Scala.

We’ll see different ways of importing types, packaging concepts, and some powerful packaging features.

2. Importing in Scala

Import statements bring the required classes and types into the scope of this file. In this section, we will explore different ways to import classes.

2.1. Basic Import

Importing a single class is pretty straightforward and similar to other object-oriented languages such as Java:

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

In case we want to import every class in a package, we can use the “_” notation:

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

We can also specify the classes we want to import in a single line:

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

2.2. Aliasing Imports

Sometimes there are cases where we have to import modules with the same name from different packages:

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

In this case, we have different date classes that return different date formats. java.sql.Date displays the date without time information whereas java.util.Date displays date and time information.

In order to avoid namespace collisions or confusion, we need to set aliasing of classes in the import statements:

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

Our aliases are now usable as any other object name:

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 !")
}

Let’s see an example of what our class could output:

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. Hiding a Class During Import

During import, we can hide a particular class from a package while importing all other classes of the same package. Let’s look at how we can hide only the java.util.Date class from java.util package:

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

Now we can create a date object without worrying about any collisions:

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. Packages in Scala

Scala provides multiple ways to define packages. Let’s look at them in this section.

3.1. Simple Package

We can create Scala packages in the same way as we do in Java. We have to declare the package name at the top of the class:

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. Multiple Packages in the Same File

We can place multiple packages in one file using curly braces. In this case, we can have two classes with similar names in different packages:

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 !")
    }
  }
}

We can refer to the first type as com.baeldung.scala.packageimport.vehicle.Vehicle and the second type as com.baeldung.scala.packageimport.vehicleNew.Vehicle.

3.3. Package Stacking

We can write multiple package statements at the top of the class and stack them together. This is similar to writing nested package statements. Let’s look at an example:

package com.baeldung.scala
package packageimport
package stacking

The above package statements are equivalent to package com.baeldung.scala.packageimport.stacking. However, the difference is that now we can access any classes from each of the stacked packages without additional import statements.

This avoids the need to use import com.baeldung.scala._ to import all the classes from that package. Similarly, we can access the classes from the package com.baeldung.scala.packageimport without any additional import statements.

4. Package Objects

Package objects allow us to keep methods, functions, and variables directly aligned with a package. As a result, all the child members of this package would be able to access them.

For each package, we are only allowed to create one package object where we put the source code for the package in. According to the Scala convention, we should always call it package.scala. Let’s create a package object for the package packageimport and keep the content in package.scala file:

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"
  }
}

All members of com.baeldung.scala.packageimport will be able to access year and Motor. In addition, we won’t have to import package object in any of those members:

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

5. Conclusion

In this article we looked at how we can use import, package other components, and how to use package objects.

We saw different ways to import packages, how to hide classes from import, and how to deal with importing classes that have the same name. Furthermore, we discussed different approaches to packaging.

Finally, we explored the powerful feature of package objects, which help us to encapsulate methods, variables, and other components at the top level.

As always, the code can be found over on GitHub.

Comments are open for 30 days after publishing a post. For any issues past this date, use the Contact form on the site.