ribesg
11/25/2021, 8:58 AMvalue class
? I just always name it value
and I wonder if there is another way that would make more senseJoffrey
11/25/2021, 9:00 AMDuration
value class, you could choose to represent the inner value in many ways. For instance, it could be milliseconds: Long
or nanoseconds: Double
- and you'd better give a clear name here because value
would be quite uninformative. Same goes for an Angle
value class, that could store degrees or radians. Basically any unitless class that needs to use a unit to store a value should specify the unit. That's just one example though.Marko Novakovic
11/25/2021, 9:00 AMCompose
when am defining routes as value class
I name property route
.
for example:
@JvmInline
value class Route(val route: String) {
companion object {
val Main = Route("main")
val Dialog = Route("dialog")
}
}
Joffrey
11/25/2021, 9:04 AMvalue
is ok of course, when there is no more information to bring. For instance, value class Password(val value: String)
. But even here we could argue that Password(val clearText: String)
would be nicer.ribesg
11/25/2021, 9:05 AMid
value
everywhere for the wrong reason, like “I know it’s a value class
so I know I need to call value
on it to get the actual value”, but you shouldn’t really care that it’s a value class
when using it I think. Just like if it was a typealias
, but with type checkJoffrey
11/25/2021, 9:09 AMbut you shouldn’t really care that it’s aExactly. That's precisely why I think it's nice to choose a property name that makes sense when you read it.when using it I thinkvalue class
password.clearText
is nice, password.value
is OK (slightly more ambiguous, but I think everyone gets it).
But it's always on a case-by-case basis for me: userId.value
may actually be less weird than userId.id
when using it, so maybe value
is not that bad here.
Even worse, imagine you have data class User(val id: UserId)
. I would hate to read user.id.id
so IMO in this case user.id.value
is better.ribesg
11/25/2021, 9:14 AMeventId.id
Michael de Kaste
11/25/2021, 2:38 PMJoffrey
11/25/2021, 2:48 PMInt
or Long
to avoid messing up what can be combined with what. It also prevents passing a distance to method that expects a duration. This is the type safety benefit.
And if you're hardcore you can even define extension functions like
operator fun Length.times(other: Length): SurfaceArea
operator fun Speed.times(time: Duration): Distance
operator fun Distance.div(time: Duration): Speed
And you never get lost about the dimensions you're working with. It's really interesting as opposed to using regular primitive types.
Another example could be using a Password
class instead of a String
to avoid mixing passwords with other sorts of texts.
However, wrapping all of those into data class Password(val value: Int)
might have a non-negligible cost at runtime due to the boxing of primitives into objects. Value classes are a way to tell the compiler that you don't care about the identity of your objects (just equality of the value inside), and thus the compiler can optimize the boxes away in many cases.Michael de Kaste
11/25/2021, 2:56 PMgildor
11/26/2021, 3:13 AMI would hate to readI think it’s a less a problem if you unwrap your value classes only when you need an actual value and most of business logic works with value class itselfuser.id.id
ribesg
11/26/2021, 9:13 AMid.id
still triggers me 😛gildor
11/26/2021, 9:17 AM!!
😄ribesg
11/26/2021, 9:37 AMid.value
, still don’t like it, so it works anyway 🙃Daniel
11/27/2021, 9:03 PMUserId(val rawId: String)