Как написать следующий Kotlin контракт? - PullRequest
1 голос
/ 02 апреля 2020

Вопрос очень прост: ( с использованием 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
)

Чтобы не иметь операторов !!, я хотел бы написать контракт это помогает компилятору выводить смарт-трансляцию, в которой говорится, что атрибуты lat и lng не равны нулю, но я не смог сделать это успешно.

Я пробовал что-то безуспешно и верю в это может быть потому, что я не совсем понимаю динамику контрактов. Это стиль:



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? Если да, то как?

Ответы [ 2 ]

1 голос
/ 02 апреля 2020

В настоящее время это невозможно. Контракт не может быть основан на свойствах класса в контракте, поэтому при проверке latitude или longitude в контракте это не разрешается.

0 голосов
/ 02 апреля 2020

Вы можете сделать что-то вроде этого:

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)

...