1

问题很简单:(使用 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 版本完成吗?如果是这样,怎么做?

4

3 回答 3

3

目前这是不可能的。合同不能基于合同中类的属性,所以当你检查latitudelongitude在合同中时,这是不允许的。

于 2020-04-01T22:28:21.423 回答
0

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

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

另请注意,这是类的构造函数,不需要构建器 :) 这适用于 Kotlin 中的所有调用。

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

于 2020-07-22T06:25:59.027 回答
-1

你可以这样做:

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

但是你不会有空值的编译时间异常。这意味着它不会强制您设置两个属性(lat 和 lng)

于 2020-04-02T00:50:33.913 回答