
Learn through the super-clean Baeldung Pro experience:
>> Membership and Baeldung Pro.
No ads, dark-mode and 6 months free of IntelliJ Idea Ultimate to start with.
Last updated: June 5, 2024
Scala’s package system keeps the code organized, but referencing packages can get tricky with relative imports. In this tutorial, we’ll explore a special syntax in the import clause using the _root_ keyword.
In Scala, organizing code with packages is a fundamental practice. However, referencing these packages can get tricky due to relative imports, especially in large projects. This is where the _root_ identifier becomes handy to avoid confusion and ensure the correct type is imported.
We can use a relative or absolute path when importing packages in Scala. Let’s explore this with an example:
package com.baeldung.scala.rootimport.scala
class List(val num: Int) {
override def toString: String = s"MyList[${num}]"
}
object List {
def apply(num: Int) = new List(num)
}
In the above code, we created a custom class List within the package com.baeldung.scala.rootimport.scala. Now, let’s write a simple test to use this List implementation:
import scala.*
val myList: scala.List = List(100)
assert(myList.toString == "MyList[100]")
This test class is in the com.baeldung.scala.rootimport package, one layer above where our custom List is defined. When we use the statement import scala.*, the Scala compiler first looks at the packages relative to the current position. As a result, it imports our custom List implementation and creates an instance of that, even though Scala Collection List is available using implicit import through Predef.
But what if we want to use the standard Scala collection List instead? To avoid using the relative path for the import, we can use the _root_ keyword:
import _root_.scala.*
val scalaList: List[Int] = List(100)
assert(scalaList.toString == "List(100)")
Here, instead of using import scala.*, we used import _root_.scala.*. This forces the compiler to use the absolute path of the package regardless of the current package, ensuring it imports the standard Scala collection List.
If we only use import scala.* here without the _root_ prefix, the compiler will utilize our custom implementation of List as it matches the relative path. We can verify that with a test:
import scala.*
val scalaList = List(100)
assert(scalaList.toString == "List(100)")
When we run this test, it fails with the error:
org.scalatest.exceptions.TestFailedException: "[MyList[100]]" did not equal "[List(100)]"
This happens because our custom List is being used, while the assert statement expects the List from the Scala collection.
The advantages of using the _root_ import are:
While _root_ offers precision and clarity, overuse of it can hinder readability.
In this brief tutorial, we examined using the _root_ identifier in Scala import statements. It is a powerful tool to manage package namespaces effectively, especially in large projects where package names might overlap. By using _root_, we can ensure precise and unambiguous imports, making the code robust and maintainable and preventing conflicts with the standard Scala library.