What is Open Class in Scala 3?

Reading Time: 2 minutes

At Knoldus, we are pursuing to learn and share Scala 3 features with the community. In that quest, I am writing this blog to talk about a new feature being introduced in Scala 3 – Open Class. You can read about our other blogs on Scala3 here.

Story till Scala2?

In Scala 2, all classes are by default available for extensions, and we need to mark them final in case we do not want them to be extended. This means that the responsibility to assess rightly and then extend a class falls on the users of the class.

Let me explain the downside of this with an example. Lets say I am the author of a library with a class A. This class is not final but I also did not intend for this class to be extended. You as the user of this library have extended A in your code. In the subsequent releases of the library, I change A, and as you upgrade, it is a breaking change for you. You cannot upgrade without fixing the breaking change. This happened because I could not specify my intention that A should not be extended using a language construct. A good case to not mark a class final and yet it is not intended for extension is want to use that class in tests.

Aside: As a good developer, one should always prefer composition over inheritance.

Open Class in Scala3

An open modifier on a class tells the users that this class is “open” for extensions. Please note that abstract classes and traits are by default considered open in Scala3, so marking them open will be redundant. Hence this feature applies to a concrete class. A class that is not marked open can still be extended but only if at least one of the conditions should be true –

  1. The extending class and the extended class are in the same file. A similar concept as sealed classes.
  2. The language feature adhocExtensions is enabled for the extending class. This can be done by a simple import.
    import scala.language.adhocExtensions

The introduction of this feature puts the onus on the author of the library to explicitly specify the intent of a class. Whether it can be extended or not. It will also ensure that the contracts are honored and breaking changes are avoided in releases. The users will get a clear visualization and then if users extend a non-open class, then they are on their own.

Note: If you do not specify the class as open and then it is extended outside the file where it was declared, it is still possible. However, you will get the feature warning from the compiler. This is how open classes are different from sealed classes. Sealed classes cannot be extended anywhere else but in the file they are declared.

Aside: If you come from Kotlin background, you will find a familiarity with this concept of final. However, in Kotlin a non-open class cannot be extended anywhere, not even in the same file. It is a compilation error.

Example

I have a concrete class Promotion which defines the discount factor.

open class Promotion {
def promotionDiscountFactor: Int = 2
}
view raw Promotion.scala hosted with ❤ by GitHub

I have marked Promotion open which would make the following possible.

class StudentPromotion extends Promotion {
override def promotionDiscountFactor: Int = 4
}

This is it for this blog. Thank you for reading and be hooked to this space for more upcoming blogs on Scala3.

References
https://dotty.epfl.ch/docs/reference/other-new-features/open-classes.html
https://contributors.scala-lang.org/t/sip-public-review-open-classes/3888/28
https://medium.com/scala-3/opaque-type-aliases-and-open-classes-13076a6c07e4

Knoldus-blog-footer-image