Note I've looked at the following questions/answers to solve the problem without any luck. Call Java Varargs Method from Kotlin - this one has the varargs parmeter at the end of the parameter list, but my question deals with varargs at the start of the parameters list. Kotlin: Convert List to Java Varargs - the same. Other searches yield the same thing. These were the closest I could find.
I am calling the Kotlin String.split
method with a single character delimiter.
This is a vararg
method where the vararg
parameter is first of multiple parameters. The method is defined like so:
public fun CharSequence.split(vararg delimiters: Char,
ignoreCase: Boolean = false,
limit: Int = 0): List<String>
When I call the method as below, it compiles fine:
fun String.splitRuleSymbol() : String = this.split(':') //ok
But when I try to add the ignoreCase
and limit
parameters, I get a problem:
fun String.splitRuleSymbol() : String = this.split(':', true, 2) //compiler error
The error I get is...
None of the following functions can be called with the arguments supplied:
public fun CharSequence.split(vararg delimiters: String, ignoreCase: Boolean = ..., limit: Int = ...): List defined in kotlin.text
public fun CharSequence.split(vararg delimiters: Char, ignoreCase: Boolean = ..., limit: Int = ...): List defined in kotlin.text
To me, having a vararg
parameter followed by other parameters is somewhat odd, but that's beside the point. If I call it as below, it works fine:
// both of the following compile
fun String.splitRuleSymbol() : String =
this.split(delimiters = ':', ignoreCase = true, limit = 2)
fun String.splitRuleSymbol2() : String =
this.split(';', ignoreCase = true, limit = 2)
Is there a way to pass a vararg Char
in to this method without having to qualify my other two parameters with parameter names ignoreCase
and limit
? Can the compiler not tell that the remaining parameters are not Char
?
I have tried the spread operator and a few other ways below , none of which work:
//compiler errors on all these
this.split(*':', true, 2) //using the "spread" operator
this.split(*charArrayOf(':'), true, 2)
this.split(*mutableListOf(':'), true, 2)
this.split(*Array<Char>(1) { ':' }, true, 2)
Yes, some of these look ridiculous, I know. But, is there no way to avoid the verbose alternative?
PS As I was formulating my question, I found another expression that compiled.
this.split(':', limit = 2)
This is less verbose and since I don't need to change the default ignoreCase
parameter, it's closer to what I am looking for.
Your observations are correct. Arguments that are after a vararg
parameter can only ever be passed in by using named arguments, otherwise you'd run into ambiguity issues (for a trivial example, let's say when all arguments are of type Any
).
The best source I can find for this right now is this book.
The vararg parameter is usually the last parameter, but it does not always have to be. If there are other parameters after vararg, then arguments must be passed in using named parameters
Edit: @Les found a good source on it, see their answer.
Thanks to zsmb13, I was able to find the following paragraph in the Kotlin Specification (under "Functions and Lambdas")
Only one parameter may be marked as vararg . If a vararg parameter is not the last one in the list, values for the following parameters can be passed using the named argument syntax, or, if the parameter has a function type, by passing a lambda outside parentheses.
I would venture to add that "can be passed" should be changed to "must be passed" since the compiler won't allow otherwise.
Note The lambda part is interesting in that the spec normally only allows a lambda to be moved outside the parenthesis when it is the last parameter. The wording of the spec implies the lambda could be anywhere after the vararg
parameter, but experimentation shows that it cannont, i.e., it must be the last parameter in order to be eligible to move outside of the parenthesis.
fun main(args: Array<String>) {
test("hello", limit = 1, ic = false, delims = ';') { } //ok
//test2("world", limit = 1, ic = false, delims = ';') { } //error
test2("world", f = {}, limit = 1, ic = false, delims = ';') //ok
test("hello world", ';', limit = 1, ic = false) {} //ok
}
fun test(vararg delims: Char, ic: Boolean, limit: Int, f: () -> Unit) {}
fun test2(vararg delims: Char, f: () -> Unit, ic: Boolean, limit: Int) {}
Variable number of arguments (vararg) can be passed in the named form by using the spread operator:
fun foo(vararg strings: String) { /* ... */ }
foo(strings = *arrayOf("a", "b", "c"))
foo(strings = "a") // Not required for a single value
Note that the named argument syntax cannot be used when calling Java functions, because Java bytecode does not always preserve names of function parameters.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With