scalainheritancevalue-class

scala value class multiple inheritance


I have in my project objects that represent IDs.

Let's say it is ChairId, TableId, LampId. I want them all to inherit from GenericId. And I want to be able to call def f(x: GenericId) = x.id

I want them to hold only single id: String so I would like to make them extend AnyVal.

Also I would like for each type to provide function generate which would generate my specific ID i.e. I would like to type something like ChairId.generate()

I have typed this:

sealed abstract class GenericId(val id: String)
final case class ChairId(override val id: String) extends GenericId(id)
final case class TableId(override val id: String) extends GenericId(id

And I though if GenericId would inherit from AnyVal that would work but so far no luck ;/ I also tried making GenericId a trait and make case classes extend AnyVal with GenericId but also won't compile :/

Another thing with TableId.generate() I can provide companion object just with function generate and that basically solve my problem but I wondered if there is possibility to solve that without defining companion object? (i.e. through implicits somehow)

// edit

regarding comment to provide code which doesn't compile(and I would like to):

sealed abstract class AbstractId(val id: String) extends AnyVal
final case class CatId(override val id: String) extends AbstractId(id)
final case class DogId(override val id: String) extends AbstractId(id)

Solution

  • Value classes cannot work this way for a couple of reasons.

    First, from the documentation, value classes cannot be extended by any other class, so AbstractId cannot extend AnyVal. (Limitation #7)

    scala> abstract class AbstractId(val id: String) extends AnyVal
    <console>:10: error: `abstract' modifier cannot be used with value classes
           abstract class AbstractId(val id: String) extends AnyVal
                          ^
    

    Second, even if you make AbstractId a trait, and define the other ids like this:

    final case class DogId(val id: String) extends AnyVal with AbstractId
    

    .. the usage of the value class wouldn't fit your case, because the class itself would still get allocated. See the allocation summary:

    A value class is actually instantiated when:

    1. a value class is treated as another type.
    2. a value class is assigned to an array.
    3. doing runtime type tests, such as pattern matching.