Scala - Packages and Imports


Scala uses packages to create namespaces. So, you can modularize programs and prevent naming conflicts. Import statements bring required classes and types into scope. We will discuss packaging and imports in this chapter.

Creating a Package

Packages in Scala are declared at the top of a Scala file using the package keyword. Packages group related classes, traits, and objects together. So, you can organize code and control scope.

Basic Package Declaration

This is simple package declaration in Scala -

package users

class User

By convention, package names should be all lowercase, and the directory structure mirrors the package hierarchy. For example, the directory structure for the above package looks like this -

- ExampleProject
  - build.sbt
  - project
  - src
    - main
      - scala
        - users
          User.scala
          UserProfile.scala
          UserPreferences.scala
    - test

Nested Packages

You can also nest packages. It provides greater control over scope and encapsulation. You can nest packages in two ways -

1. Using curly braces

package users {
  package administrators {
    class AdminUser
  }
  package normalusers {
    class NormalUser
  }
}

2. Using colons and indentation (Scala 3 syntax) -

package users:
  package administrators:
    class AdminUser

  package normalusers:
    class NormalUser

Package Naming Conventions

There is some naming conventions for code developed within an organization. You should use the organization domain name in reverse as part of the package name. For example,

package com.tutorialspoint.selfdrivingcar.camera

class Lens

This will correspond to the directory structure -

SelfDrivingCar/src/main/scala/com/tutorialspoint/selfdrivingcar/camera/Lens.scala

Importing in Scala

Import statements bring required classes, traits, objects, and methods into scope. Scala import technique is more flexible than Java import technique. You can import anywhere in the code and provide features like renaming and hiding members.

Basic Imports

You can import a single class, multiple classes, and even all classes from a package. For example -

import users.User
import users.{User, UserPreferences}
import users._

Aliasing Imports

You can rename imported members to avoid naming conflicts and to improve clarity. For example -

import java.util.{Date => UtilDate}
import java.sql.{Date => SqlDate}

Hiding Members on Import

You can hide specific members while importing the rest of the package. For example -

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

Importing Static Members

You can import static members of a class directly. For example −

import java.lang.Math._

val a = sin(0)
val b = cos(PI)

Importing Givens (Scala 3)

In Scala 3, you can import given instances (similar to implicit values in Scala 2). For example -

object A:

  given tc: TC
  def f(using TC) = ???

object B:
  import A.{given, *}

Packages in Scala

Multiple Packages in the Same File

There can be multiple packages in a single file using curly braces in Scala. For example -

package users {
  package administrators {
    class AdminUser
  }
  package normalusers {
    class NormalUser
  }
}

Package Stacking

You can stack package declarations to create nested packages without curly braces. For example -

package com.tutorialspoint.scala
package packageimport
package stacking

You can access classes from any of the stacked packages without more import statements.

Using Packages in Different Files

You can add members to the same package from different files in Scala. It is used for organizing large codebases. For example -

// file: faculty.scala
package college

class Faculty {
  def facultyMethod() {}
}

// file: student.scala
package college

class Student {
  def studentMethod() {}
}

// file: main.scala
package college

object Main {
  def main(args: Array[String]): Unit = {
    val student = new Student()
    val faculty = new Faculty()
    // Methods from both Student and Faculty can be used here
  }
}

Import Statements Anywhere

Scala's import statements can be placed anywhere in the code, not just at the top of the file. So you can have more granular control over imports. It reduces scope pollution. For example -

package foo

class ClassA {
  import scala.util.Random

  def printRandom(): Unit = {
    val r = new Random
    println(r.nextInt())
  }
}

class ClassB {
  // Random is not visible here
  // val r = new Random // This will not compile
}

Package Objects

You can define functions, variables, and types that are accessible to all members of the package. Each package can have one package object, typically named package.scala. For example -

Example

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

Members of the com.tutorialspoint.scala.packageimport package can now access year and Motor without importing these.

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

Combining Different Import Techniques

Scala has flexible import system. So, you can combine various import techniques for more readable and maintainable code. For example -

package demo

import java.util.{Date => UtilDate}
import java.sql.{Date => SqlDate}

object Demo {
  def main(args: Array[String]): Unit = {
    val utilDate = new UtilDate()
    val sqlDate = new SqlDate(System.currentTimeMillis())

    println(s"Util Date: $utilDate")
    println(s"SQL Date: $sqlDate")
  }
}

Packages and Imports Summary

  • Scala uses packages to create namespaces. So, you can modularize code and prevent naming conflicts.
  • Import statements can be used anywhere in the code and for flexible importing, renaming, and hiding of members.
  • Package objects let you define methods, variables, and types that are accessible to all members of the package.
  • Givens in Scala 3 can be imported using the given
  • You can use package stacking to create nested packages without more import statements.
  • You should use naming conventions to organize packages and keep clean directory structure.
  • Scala import system is more flexible than Java import system for better control over namespace and scope.