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

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

In case we want to use the only java.sql.date format, but we need to import all modules from java.util, we will then need to hide java.util.date:

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

Creating Scala packages is similar to the practice we all know in Java. We have to declare the package name on 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"
  }
}

Furthermore, we have the possibility to place multiple packages in one file using curly braces. In this case, we can have two classes with similar names on 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 !")
    }
  }
}

Using this approach, we can either use com.baeldung.scala.packageimport.vehicleNew.Vehicle or com.baeldung.scala.packageimport.vehicle.Vehicle.

Next, let’s try and access the second class:

class Car(numberOfTires: Int, brand: String, isDiesel: Boolean) extends Vehicle(numberOfTires, brand) with Motor {
  override def run(): Unit = {
    super.run()
    println(s"$numberOfTires tires from the brand $brand are running. It is a diesel motor: $isDiesel.")
    if (isDiesel)
      println(dieselMessage)
  }
}

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.

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.package 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 tutorial, we had a look at how we can use import, package other components, and how to use package objects.

We looked at different ways to import packages, how to hide classes from import, and how to deal with importing classes with have the same name. Furthermore, we took a look at different approaches to packaging.

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

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

guest
0 Comments
Inline Feedbacks
View all comments