如何撰写以下Kotlin合同



问题很简单:(使用Kotlin 1.3.71(

我有以下与此类似的数据:

data class Location(val lat: Double, val lng: Double)

我想通过这样的调用实现类型安全:

val loc = location {
lat = 2.0
lng = 2.0
}

为了实现这一点,我建立了:


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
)

为了避免使用!!运算符,我想编写一个契约,帮助编译器推断一个智能广播,说明属性latlng不为空,但我未能成功做到这一点。

我尝试过一些没有成功的事情,我相信这可能是因为我没有完全理解合同的动态。这些是的风格


fun LocationBuilder.buildSafely(dsl: LocationBuilder.()->Unit): LocationBuilder {
contract {
returnsNonNull() implies (this@buildSafely.lat != null && this@buildSafely.lng != 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 */
}

所以问题是:

这可以用当前的Kotlin版本完成吗?如果是,如何

这目前是不可能的。约定不能基于约定中类的属性,因此当您在约定中选中latitudelongitude时,这是不允许的。

您可以将"{}"替换为"(("并使用命名参数:

val loc = Location(
lat = 2.0
lng = 2.0
)

还要注意,这是类的构造函数,不需要构造函数:(这适用于Kotlin中的所有调用。

请参阅https://kotlinlang.org/docs/reference/functions.html#named-自变量

您可以这样做:

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)
}

但是对于null,您不会有编译时异常。这意味着它不会强迫你同时设置属性(lat和lng(

相关内容

  • 没有找到相关文章

最新更新