Hello,
I’m very big fan of Kotlin and it’s syntax improvements over Java (such as zero-boilerplate delegation and zero-boilerplate singletones).
Recently I got interested in zero-boilerplate builders.
I believe that builder pattern exists to overcome following language limitations:
- lack of named arguments
- lack of default and optional arguments
- lack of multiple vararg arguments
And when first two limitations don’t exists in Kotlin I’ll try to resolve third.
Also I want to share idea how to implement collection literals.
It’s separate feature, but have common component with vararg - arrays.
Arrays - will be focus of this article.
NOTE:
Maybe this plan is too complex or idealistic to be implemented.
I split it to parts and some can be skipped\changed to preserve backwards compatibility or keep simplicity.
At least if it inspire someone I’ll be happy.
Also my English is weak. Sorry about that
These are steps to do:
- Rename
Array<T>
to[T]
.
- because it makes array types special like lambda types and it’s important on next steps
- it should look like this:
val arrayDeclarationExample: [String]? = null
val genericsProjectionsExample: [out String]? = null
val ThreeDimensionalExample: [[[Int]]]? = null
fun arrayParameterExample(array: [Byte?]) { }
fun arrayInLambdaExample(block: (() -> [Long])) { }
fun arrayInLambdaExampleReceiver(block: [Char].(([Char]) -> Unit) { }
fun getStringArray(): [String] = ["String"]
fun getIntArray() = [1]
- Imlement
array literals
- because it’s useful, wanted feature and it’s required on next steps
- proposed syntax is:
val arrayLiteralExample = ["hello", "array"]
val arrayLiteralExampleEmpty: [String] = []
val arrayLiteralExampleThreeDimensional = [[[]], [[], []], []] //Any
arrayParameterExample([42, 42])
arrayParameterExample([])
arrayInLambdaExample({ [] })
arrayInLambdaExample({ [1, 2, 3] })
arrayInLambdaExample { [1, 2, 3] }
- Introduce
vararg arrays
instead ofvararg keyword
- difference with varargs is that multiple vararg array arguments are allowed
- difference with arrays is that spread operator is required when array is created not in place
- Java varargs are converted to Kotlin vararg arrays automatically
- syntax should look like this:
fun takesVarargArrays(a: vararg [Int], b: vararg [String])
takesVarargArrays([1,2,3], [])
takesVarargArrays(*getIntArray(), *getStringArray())
- Relax syntax rules on vararg arrays to encourage users to use them in right places
- 1) vararg array arguments are default to empty arrays
- 2) braces on vararg array literal can be ommitted with single value
- and here are examples of legal expressions:
takesVarargArrays()
takesVarargArrays([])
takesVarargArrays(b = [])
takesVarargArrays(a = 1)
- Apply last argument convention for vararg array literals
- it works well for higher-order functions and makes Kotlin so awesome!
- also, it’s known and simple rule - no re-inventing the wheel, no surprises
- example
fun lastArgumentConventionExample(dummy: String, a: vararg [Int]) { }
lastArgumentConventionExample("text") [1, 2, 3, 4, 5]
fun lastArgumentConventionExampleSingle(a: vararg [Int]) { }
lastArgumentConventionExampleSingle [1, 2, 3, 4]
- collection literals now are very easy to implement, some are already implemented
val listExampleGeneric = listOf<String> []
val listExampleGenericSameAsPrevious = List<String> []
val mapExample = Map [
0 to false,
1 to true
]
val arrayListExample = ArrayList ["constructor", "inferred", "from", "JVM"]
val arrayListExampleHint = ArrayList(4) [""]
val rxExample = Observable.just [1, 2, 3]
- Update index access and index assign operators to work after dot
- this should resolve some inconsistencies with changes introduced in previous steps
- example
val indexAccessExample = arrayListExample.[0]
ArrayList[1, 2, 3].[0] = 3
Map<String, Any>[].["key"]
And result should be following:
annotation class AnnotationExample(val args: vararg [String])
@AnnotationExample[ "hello", "annotation" ]
@get:AnnotationExample
@set:[AnnotationExample(args = "single")]
val annotationUsageExample = 1
class WithoutBuilder(
val name: String,
val array: [String],
val optional: Int?,
val variadic: vararg [String],
val nested: [WithoutBuilder]
)
val withoutBuilderExample = WithoutBuilder("exampleParent",
array = [],
optional = 100,
nested = WithoutBuilder("exampleNested"
array = ["", "", ""],
variadic = "single",
nested = WithoutBuilder("exampleNestedDeep", [], 100,
variadic = ["a", "b", "c"],
nested = WithoutBuilder("exampleNestedLast",
array = getStringArray(),
variadic = *getStringArray())
)
)
)