Reputation: 1677
Consider the following code:
val key: String? = "key"
val value: String? = "label"
val row = key + ": " + value
I would like the variable row
to be null
if any of the supplied inputs in the concatenation is null
.
By default, any null
String will be converted to "null"
and the concatenation will proceed. In example:
val value = null
"Height: " + value + "mm" // Produces: "Height: nullmm"
I can skip the showing "null"
in the results by using value ?: ""
, but this solves only a part of my problem:
val value = null
"Height: " + (value ?: "") + "mm" // Produces: "Height: mm"
I understand that writing a simple function like the one below would do the job, but I still expect that something like this already exists in the language:
fun Array<String?>.nullSafeConcat(): String? {
val result = StringBuilder()
this.forEach {
if(it == null) return null
result.append(it)
}
return result.toString()
}
Is there a better way to do this?
I cannot understand why would a null string be converted to "null"
by default, as I cannot find any use case where this would be actually usable.
Upvotes: 0
Views: 946
Reputation: 5090
I'm not sure if this solves the problem, you can override the +
operator on a nullable String
to get close to what you want. For example:
private operator fun String?.plus(otherString: String?): String? = if (this==null || otherString ==null ) "null" else this + otherString
Then:
fun main() {
val s1: String? = "Hello"
val s2: String? = null
val s3: String? = "Bye"
println(s1 + s2)
println(s2 + s1)
println(s1 + s3)
}
prints:
null
null
HelloBye
The problem is it will only work with variables of String?
not String
which your ":"
value is. So you'd need to do something like:
s1 + colon + s2
where colon was also of type String?
EDIT:
There are two dangers with this approach. Firstly If you don't make it private (or even if you do) there is a risk that existing or new code tries to append two String? values and gets your new behaviour, which they don't expect. Secondly, someone reading the code where you call it may be surprised by the behaviour if they don't spot that you've overridden the +
operator.
Upvotes: 2
Reputation: 436
How about this
listOfNotNull("foo", null, "bar", "baz").joinToString()
Upvotes: 0
Reputation: 7725
I think matt freake's answer is correct for the question, but I would avoid overriding the +
operator for Strings - it can cause tons of unexpected issues.
Instead, I suggest you to slightly modify your nullSafeConcat
helper function to be a standalone function that takes vararg
instead of being an extension function. Something like this:
fun nullSafeConcat(vararg strings: String?): String? {
val result = StringBuilder()
strings.forEach {
if(it == null) return null
result.append(it)
}
return result.toString()
}
Then you can use it like:
val merged = nullSafeConcat("foo", null, "bar", "baz")
Notes:
You might want to handle the empty case (when varargs argument strings
is empty) specifically, depending on the outcome you want.
Additionally, if you want this to work for at least 2 strings (so a concatenation is actually meaningful), you can use a signature like nullSafeConcat(first: String?, second: String?, vararg rest: String?)
instead.
Upvotes: 2