Reputation: 8457
The question is very simple: (using Kotlin 1.3.71)
I have the following data alike this one:
data class Location(val lat: Double, val lng: Double)
I want to achieve a type-safety with a call like so:
val loc = location {
lat = 2.0
lng = 2.0
}
To achieve so I built:
fun location(builder: LocationBuilder.() -> Unit): Location {
val lb = LocationBuilder().apply(builder)
return Location(lb.lat!!, lb.lng!!)
}
data class LocationBuilder(
var lat: Double? = null,
var lng: Double? = null
)
To avoid having !!
operators I would like to write a contract that helps the compiler infere a smartcast that says that the attributes lat
and lng
are not null but I have not been able to do that successfully.
I've tried things without success and I believe it might be because I am not fully understanding the dynamics of contracts. Those are the style of:
fun LocationBuilder.buildSafely(dsl: LocationBuilder.()->Unit): LocationBuilder {
contract {
returnsNonNull() implies ([email protected] != null && [email protected] != null)
}
apply(dsl)
if(lat == null || lng == null) throw IllegalArgumentException("Invalid args")
return this
}
fun location(builder: LocationBuilder.()->Unit): Location {
val configuredBuilder = LocationBuilder().buildSafely(builder)
return Location(configuredBuilder.lat, configuredBuilder.lng)
/* I would expect a smart cast but I am getting a compile error stating that lat and lng may still be null */
}
So the question is:
Upvotes: 1
Views: 928
Reputation: 44
You could just replace the '{}' for '()' and use named arguments:
val loc = Location(
lat = 2.0
lng = 2.0
)
Also note that this is the constructor of the class, no builder needed :) This works for all calls in Kotlin.
See https://kotlinlang.org/docs/reference/functions.html#named-arguments
Upvotes: 1
Reputation: 542
You can do something like this:
import kotlin.properties.Delegates
fun location(builder: LocationBuilder.() -> Unit): Location {
val lb = LocationBuilder().apply(builder)
return Location(lb.lat, lb.lng)
}
class LocationBuilder {
var lat by Delegates.notNull<Double>()
var lng by Delegates.notNull<Double>()
}
data class Location(
val lat: Double,
val lng: Double
)
fun main() {
val l = location {
lat = 2.0
lng = 8.0
}
println(l)
}
But you won't have compilation time exception for the nulls. It means that it doesn't force you to set both properties (lat and lng)
Upvotes: -1
Reputation: 29260
This is currently not possible. A contract can't be based on the properties of the class in the contract, so when you check latitude
or longitude
in the contract, that's not allowed.
Upvotes: 4