Reputation: 10992
While domain modeling how can I ensure that two types — User
and Place
don't interchange their name
fields, which are of type String
.
trait User {
val firstName: String
val lastName: String
}
object User {
final class Live(val firstName: String,val lastName: String) extends User
def apply(firstName: String, lastName: String): User = new Live(firstName, lastName)
}
trait Place {
val name: String
}
object Place {
final class Live(val name: String) extends Place
def apply(name: String): Place = new Live(name)
}
val a = User("Tushar", "Mathur")
val b = Place("Mumbai")
val c = Place(a.firstName)
// How do I disable this ^
Upvotes: 1
Views: 50
Reputation: 16412
This is supported in Scala as in the example below. There are some libraries that can reduce boilerplate but I'm just showing the simplest option:
// define more restrictive String-like types. AnyVal construction can be free from
// overhead with some caveats.
case class UserName(name: String) extends AnyVal
case class PlaceName(name: String) extends AnyVal
// define your classes (I've changed them a bit for brevity):
case class User(name: UserName)
case class Place(name: PlaceName)
// implicits for convenience of construction:
implicit val strToUserName = UserName(_)
implicit val strToPlaceName = PlaceName(_)
// test it out:
scala> val u = User("user")
u: User = User(UserName(user))
scala> val p = Place("place")
p: Place = Place(PlaceName(place))
// as expected you CAN'T do this:
scala> User(p.name)
<console>:17: error: type mismatch;
found : PlaceName
required: UserName
User(p.name)
^
// comparison test:
scala> p.name == u.name
<console>:16: warning: comparing case class values of types PlaceName and UserName using `==' will always yield false
p.name == u.name
^
res3: Boolean = false
// you can still get at the string value:
scala> p.name
res6: PlaceName = PlaceName(place)
scala> p.name.name
res5: String = place
Upvotes: 2