https://kotlinlang.org logo
#announcements
Title
# announcements
c

cy

05/26/2017, 3:13 PM
Copy code
data class Color(val r: Int, val g: Int, val b: Int) {
    init {
        require(r in 0..255) { "invalid red $r" }
        require(g in 0..255) { "invalid red $g" }
        require(b in 0..255) { "invalid red $b" }
    }

    constructor(r: String, g: String, b: String) : this(r.toColorComponent("red"), 
                                                        g.toColorComponent("green"), 
                                                        b.toColorComponent("blue"))
}

private fun String.toColorComponent(name: String) = toIntOrNull() ?: throw IllegalArgumentException("Color component $name string is not a number: $this")
p

pawel.barszcz

05/26/2017, 3:14 PM
cy: hmm, so in your solution I have to accept that I have that primary constructor still accessible with ints in it.
c

cy

05/26/2017, 3:14 PM
in fact it is big advantage
p

pawel.barszcz

05/26/2017, 3:14 PM
in my real case I have
ActivityId
and it is long in fact, but I do not want to expose such knowledge
so I want to operate on String
k

kevinherron

05/26/2017, 3:16 PM
make them private then
c

cy

05/26/2017, 3:16 PM
most likely it's not good idea to keep it as string
p

pawel.barszcz

05/26/2017, 3:16 PM
oh, is a long story. The fact that it is a long is a implementation detail of how we store it in one database in one service. But orher services and other code don’t have to assume such fact
BTW: I think I have to try now with
init { ...}
before asking more questions
beause at the moment my questions are too blurry, let’s say
so thanks for pointing out the
init { ... }
and I will come back if I see some real disadvantages of using it 🙂
OK, one more case. What would you say, @cy and @kevinherron ?
Copy code
data class ActivityName private constructor(
        val value: String
) {

    companion object {

        const val MAX_NAME_LENGTH: Int = 1_000

        fun of(value: String?): ActivityName {
            if (value == null) throw ActivityNameIsEmptyException()
            return value.trim().let {
                if (it.isEmpty()) throw ActivityNameIsEmptyException()
                if (it.length > MAX_NAME_LENGTH) throw ActivityNameIsTooLongException()
                ActivityName(it)
            }
        }

    }

}
In other words: I want to provide possibility to construct my object from nullable
String?
. Other way I would have to check for null outside the object, and check for emptiness inside the object, while both cases are in fact checking for semantical emptiness
but I cannot create two cosntructoes, one with
String
, and another one with
String?
because of clashing signatures
and ofc I do not want my data class property to be nullable, because in the end my data class cannot contain null (or maybe I should act as it could have null?)
c

cy

05/26/2017, 3:35 PM
you can declare a function named
ActivityName
fun ActivityName(name: String?) = if (name == null) throw IlegalArgumentException("...") else ActivityName(name)
of course you should keep constructor's parameter non-nullable
However I believe it's not good at all as you simply trying to make your code unsafe
don't eliminate null safety, use it
p

pawel.barszcz

05/26/2017, 3:37 PM
do you mean such solution?
Copy code
data class ActivityName(
        val value: String
) {
// ...
}

fun ActivityName(value: String?) {
    return ActivityName(value)
}
c

cy

05/26/2017, 3:38 PM
yes, but
return if (value != null) ActivityName(value) else throw
p

pawel.barszcz

05/26/2017, 3:39 PM
right, I forgot to put
// ... validation ehre
🙂
in such case my IDE sees
ActivityName
function instead of data class. I mean it can be a bit confusing to operate on both
even if it is doable
c

cy

05/26/2017, 3:40 PM
however you have a safe constructor and an unsafe function with the same name
p

pawel.barszcz

05/26/2017, 3:40 PM
don’t eliminate null safety, use it
For simplicity (even if I can make it better) let’s say that I don’t know whether my HTTP request body contains name of activity or not
so I ofc can check for nulalbility before creating
ActivityName
, but then I will end up with checking for null in one palce and cheking for emptiness in another
in such way I can categorize null and blank values as same “there is no activity name” and handle it in same way with same error message in HTTP response etc.
probably I’m overcomplicating in order to simplify 😛
c

cy

05/26/2017, 3:43 PM
I see, then you can simply do like you did already: make a function
of
with nullable and constructor with non-null
unfortunately you have to check for null in
of
and for empty in
init
p

pawel.barszcz

05/26/2017, 3:46 PM
hah, I have one another example, but I think it should have factory method or computed property: let’s say you constructs a color from strings like
#aabbcc
,
#abc
, and
#ABC
, but you want to to retrieve the value always normalized to lowercase, 6-characters
currently I do normalization in factory method and I do not want to give possibility o construct data class without such normalization
Copy code
data class ActivityColor private constructor(
        val hexValue: String
) {
    companion object {
        // ...
        fun of(hexValue: String?): ActivityColor {
            // validation
            return ActivityColor(normalized(hexValue))
        }
        // ...
    }
}
so if I make this counstructor public, then I probably have to validate its parameter to check whether it is already normalized. And then provide factory method which is normalizaing
(again: my goal is to get rid of IDE warnging, that my cosntructor is not in fact private, when you have
.copy(...)
on data class)
5 Views