I don't agree with your way of thinking. Java primitives can't be extended because they are primitives (btw String
is not a primitive). They are a direct representation of byte code types. Extending them would make no sense from the compiler perspective.
Implicit value classes
Scala deals with this using the pimp my library pattern, for example with the class RichInt. This permits to add new methods to an existing type (mainly) without object instantiation (through value classes and implicits). Please also have a look to implicit classes.
implicit class DomainType(val o: String) extends AnyVal {
def myDomainMethod: Int = o.size
}
"hello".myDomainMethod // return 5
Problem, this doesn't allow you to restrict a type as you would like to do with your DomainType
. But type classes can help you.
Type classes
Here we want to add a constraint to a type, without inheritance. As said in this link,
As such, type classes allow ad-hoc and retroactive polymorphism. Code that relies on type classes is open to extension without the need to create adapter objects.
The following example shows how the method foo
only accepts an argument where an implicit of type DomainType[T]
is in scope. It replaces the inheritance you wanted by a retroactive polymorphism. You also keep the benefits from a domain type: The intent is clear, the call type safe and you can add new domain methods.
trait DomainType[T] {
def myDomainMethod(o: T): Int
}
object DomainType {
implicit object StringDomainType extends DomainType[String] {
def myDomainMethod(o: String): Int = o.size
}
}
def foo[T : DomainType](p: T): Int = {
implicitly[DomainType[T]].myDomainMethod(p)
}
foo("hello") // return 5
foo(5) // compilation exception
Case classes + implicits
If the only thing that annoys you with your DomainType
case class is to call .value
, you can always add an implicit that does it for you.
implicit def unwrapDomainType(o: DomainType): String = o.value
Of course this can lower the clarity of the code and I would not recommend it.