Skip to content

Commit

Permalink
Add Scala 2 version of case classes that can evolve
Browse files Browse the repository at this point in the history
The same approach as with Scala 3 also works with Scala 2 if you add the `-Xsource:3` compiler option.
  • Loading branch information
julienrf committed Mar 30, 2023
1 parent f445e8d commit 9dca98b
Showing 1 changed file with 47 additions and 5 deletions.
52 changes: 47 additions & 5 deletions _overviews/tutorials/binary-compatibility-for-library-authors.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,21 +182,39 @@ To achieve that, follow this pattern:
* define a private `unapply` function in the companion object (note that by doing that the case class loses the ability to be used as an extractor in match expressions)
* for all the fields, define `withXXX` methods on the case class that create a new instance with the respective field changed (you can use the private `copy` method to implement them)
* create a public constructor by defining an `apply` method in the companion object (it can use the private constructor)
* in Scala 2, you have to add the compiler option `-Xsource:3`

Example:

{% tabs case_class_compat_1 %}
{% tab 'Scala 3 Only' %}
{% tab 'Scala 2' %}
~~~ scala
// Mark the primary constructor as private
case class Person private (name: String, age: Int) {
// Create withXxx methods for every field, implemented by using the (private) copy method
def withName(name: String): Person = copy(name = name)
def withAge(age: Int): Person = copy(age = age)
}

object Person {
// Create a public constructor (which uses the private primary constructor)
def apply(name: String, age: Int) = new Person(name, age)
// Make the extractor private
private def unapply(p: Person): Some[Person] = Some(p)
}
~~~
{% endtab %}
{% tab 'Scala 3' %}

```scala
// Mark the primary constructor as private
case class Person private (name: String, age: Int):
// Create withXxx methods for every field, implemented by using the copy method
// Create withXxx methods for every field, implemented by using the (private) copy method
def withName(name: String): Person = copy(name = name)
def withAge(age: Int): Person = copy(age = age)

object Person:
// Create a public constructor (which uses the primary constructor)
// Create a public constructor (which uses the private primary constructor)
def apply(name: String, age: Int): Person = new Person(name, age)
// Make the extractor private
private def unapply(p: Person) = p
Expand Down Expand Up @@ -240,7 +258,20 @@ Later in time, you can amend the original case class definition to, say, add an
* tell MiMa to [ignore](https://github.com/lightbend/mima#filtering-binary-incompatibilities) changes to the class constructor. This step is necessary because MiMa does not yet ignore changes in private class constructor signatures (see [#738](https://github.com/lightbend/mima/issues/738)).

{% tabs case_class_compat_4 %}
{% tab 'Scala 3 Only' %}
{% tab 'Scala 2' %}
~~~ scala
case class Person private (name: String, age: Int, address: Option[String]) {
...
def withAddress(address: Option[String]) = copy(address = address)
}

object Person {
// Update the public constructor to also initialize the address field
def apply(name: String, age: Int): Person = new Person(name, age, None)
}
~~~
{% endtab %}
{% tab 'Scala 3' %}
```scala
case class Person private (name: String, age: Int, address: Option[String]):
...
Expand Down Expand Up @@ -296,7 +327,18 @@ Optionally, you can also add overloads of the `apply` method in the companion ob
in one call. In our example, we can add an overload that also initializes the `address` field:
{% tabs case_class_compat_7 %}
{% tab 'Scala 3 Only' %}
{% tab 'Scala 2' %}
~~~ scala
object Person {
// Original public constructor
def apply(name: String, age: Int): Person = new Person(name, age, None)
// Additional constructor that also sets the address
def apply(name: String, age: Int, address: String): Person =
new Person(name, age, Some(address))
}
~~~
{% endtab %}
{% tab 'Scala 3' %}
~~~ scala
object Person:
// Original public constructor
Expand Down

0 comments on commit 9dca98b

Please sign in to comment.