https://kotlinlang.org logo
Title
w

Wesley Acheson

06/09/2020, 1:00 PM
Hello at all. I've got some java objects that I'd need to convert to kotlin. Is there an idiomatic way of specifying constraints on data classes. Like the following Java:
public class TotallyMadeUpClass{
    private String username
    public String getUsername(){ return this.username }
    public void setUsername(String username) {
        if (username.length > 10)
        throw new IllegalArgumentException()
        this.username = username}
}
it doesn't need to be strings, it doesn't need the illegal argument exception. I just want a way to specify the constraint in the language.
r

Ruckus

06/09/2020, 1:06 PM
class TotallyMadeUpClass(userName: String) {
  var userName = userName
    set(value) {
      require(value.length <= 10)
      field = value
    }
}
w

Wesley Acheson

06/09/2020, 1:07 PM
Okay so vars not vals. I assume also that this cannot be used as a constructor parameter? I'm also crossposting to stack overflow.
r

Ruckus

06/09/2020, 1:11 PM
Edited to show using constructor param. For future note, if you're asking on SO, it's common practice to post the question there and then just post a link here (with some context) so people can go check it out if they want. That way you don't split the discussion.
😁 1
w

Wesley Acheson

06/09/2020, 1:12 PM
Thank you
👍 1
I've put it on StackOverflow anway. As maybe will help someone else find the answer. Feel free to answer or I can copy your text into an answer. https://stackoverflow.com/questions/62283491/kotlin-data-class-enforce-value-constraints
I'll remember to do that in the future.
r

Ruckus

06/09/2020, 1:21 PM
Not a big deal, and it's not a hard requirement or anything, just something that has become a common practice of sorts.
p

Philip Puffinburger

06/09/2020, 1:22 PM
If you do want to use a data class and val’s you should be able to do:
data class TotallyMadeUpClass(val username: String) {
    init {
        require(username.length > 10)
    }
}
That should validate whether you set it from the constructor or the copy method.
2
☝️🏻 1
w

Wesley Acheson

06/09/2020, 1:22 PM
@Philip Puffinburger Perfect thats what I'm looking for. It would be even cooler if it had TypeScript like constraints but this is what I needed. I was already trying to see if I could force @Ruckus example to something like this.
Thank you both I didn't know the require keyword either.
fun main() {
    println(TotallyMadeUpClass("OK"))
    println(TotallyMadeUpClass("This username should be rejected as its too long"))
}

data class TotallyMadeUpClass(val username:String) {
    init {
        require(username.length < 10) {"Testing to see if I can get a constraint message"}
    }
}
TotallyMadeUpClass(username=OK)
Exception in thread "main" java.lang.IllegalArgumentException: Testing to see if I can get a constraint message
at com.blah.TotallyMadeUpClass.<init>(ConstraintTest.kt:10)
at com.blah.merchantapi.ConstraintTestKt.main(ConstraintTest.kt:5)
at com.blah.merchantapi.ConstraintTestKt.main(ConstraintTest.kt)
sorry about formatting
m

Matteo Mirk

06/09/2020, 1:54 PM
Here is a little variation to show how it could be done in case of a standard class, having a constructor parameter that doesn’t correspond to a property:
class Username(username: String) {
    val user = "@${username.toLowerCase()}"
    init {
        require(username.length < 10) { "user name must be shorter than 10 characters" }
    }
}

// usage
val u = Username("Joe")
u.user // @joe
notice the absence of
val/var
in the constructor argument. The read-only property
user
is initialized only after the init check passes
👍 1