is there something like `public fun String.substri...
# stdlib
c
is there something like `public fun String.substringAfter(delimiter: String, missingDelimiterValue: String = this)`` that instead returns null if the delimeter is not found?
s
Strange that the type of
missingDelimiterValue
isnt
String?
e
it isn't type
String?
because then every use would have have to deal with potential null result
s
Only if you pass in a null ie.
fun <R : String?> String.substringAfter(..., missingDelimiterValue: R = this) : R
👍 2
Same as all the others that take a default value (ie. getOrDefault() for a map)
e
Map.getOrDefault
mirrors a Java API and requires
@UnsafeVariance
due to being in in+out position. Kotlin stdlib methods take a lambda for default, e.g.
getOrElse
note that you can't actually define
fun <R : String?> String.substringAfter(..., missingDelimiterValue: R = this) : R
, give it a shot and you'll see
Copy code
ERROR Type mismatch: inferred type is String but R was expected
what you want is that
R
is a supertype of
String?
, not a subtype, but that can't be expressed
c
no, R is either String? or String so its a subtype
Copy code
private  fun <R:String?> String.substringAfter(delimiter: String, default:R): R {
    val index = indexOf(delimiter)
    return if (index == -1) default else substring(index + delimiter.length, length) as R
}

fun main() {
    val nullable : String? = "nullable".substringAfter("null", null)
    val nonNullable : String = "nullable".substringAfter("null", "notNull")
}
this works
e
mapOf<String, String>("a" to "b").getOrElse("c") { 0 } is Any
works
string.ifEmpty { charSequence }
works
supertypes should be the solution, it just can't be expressed in Kotlin
c
image.png
the method acts exactly as it should
s
I stand corrected. Thats what you get for not trying it out 😉
c
so whats wrong with the cast? the method is just as type safe as the not nullable method or what am I missing?
s
I think your version is fine. You just sacrificed the default value for the parameter to do it.
c
oh lol i totally missed that. without default parameter it does not make much sense
I’ll just keep using my own
subStringAfterOrNull
I still don’t undestand why it needs the cast at all and why r should be a supertype
also whats wrong with this?
Copy code
fun <R:String?> String.substringAfter(delimiter: String, default:R = this as R): R {
    val index = indexOf(delimiter)
    return if (index == -1) default else substring(index + delimiter.length, length) as R
}
default parameters can be cast too
e
it can't handle
.substringAfter("", charSequence)
. the existing method can't either, but Kotlin's other get-or-else all handle similar cases fine
ideally something like
Copy code
inline fun <R> String.substringAfter(delimiter: String, default: () -> R): R where String : R = ...
fun String.substringAfter(delimiter: String): String = substringAfter(delimiter) { this }
but that
where
clause is not legal
c
ok but thats a seperate issue from the nullability
Copy code
fun <R:CharSequence?> CharSequence.substringAfter(delimiter: String, default:R = this as R): R {
    val index = indexOf(delimiter)
    return if (index == -1) default else substring(index + delimiter.length, length) as R
}
s
I think you should submit an issue with the improved version:
Copy code
fun <R:String?> String.substringAfter(delimiter: String, default:R = this as R): R