Можно ли в котлине вызвать функцию с unaryPlus?
Это уточняющий вопрос по другому вопросу, который я задал вчера.
Как я могу создать вложенный список с помощью шаблона построителя?
Кредит: Pelocho за хороший ответ.
Я использовал этот учебник для создания типобезопасного построителя запросов graphQL:
Что я хочу сделать сейчас, так это упростить то, что я сделал. И я знаю, что у kotlin для этого должны быть хорошие функции.
Прямо сейчас мне нужно вызывать функции, когда я хочу добавить сущность в свой запрос:
fun main() {
events{
title() // I don't like to do () when it is an edge case
}
}
Мне интересно, можно ли сделать что-то другое и более простое, например:
fun main() {
events{
title // this is much nicer
}
}
Но если это невозможно, то можно просто уменьшить количество символов с помощью перегрузки оператора, например.unaryPlus
fun main() {
events{
+title // this is also nicer
}
}
В моем коде, который у меня есть, теперь есть класс, в Queryкоторый я помещаю метод:
operator fun Query.unaryPlus() {
visitEntity(this,{})
}
Но, похоже, у меня это не работает.
Весь мой код здесь.
interface Element {
fun render(builder: StringBuilder, indent: String)
}
@DslMarker //Domain Specific Language
annotation class GraphQLMarker
@GraphQLMarker
abstract class Query(val name: String) : Element {
val children = arrayListOf<Element>()
protected fun <T : Element> visitEntity(entity: T, visit: T.() -> Unit = {}): T {
entity.visit()
children.add(entity)
return entity
}
override fun render(builder: StringBuilder, indent: String) {
builder.append("$indent$name")
if (children.isNotEmpty()) {
builder.append("{\n")
for (c in children) {
c.render(builder, "$indent ") } builder.append("$indent}")
}
builder.append("\n")
}
operator fun Query.unaryPlus() {
visitEntity(this,{})
}
override fun toString(): String {
val builder = StringBuilder()
render(builder, "")
return builder.toString()
}
}
Затем я создал классы, относящиеся к моему делу
class Filter private constructor(val filters: MutableMap<FilterType, Any>) {
class Builder {
private val filters = mutableMapOf<FilterType, Any>()
fun filters(key: FilterType, value: Any) = apply {
this.filters[key] = value
}
fun build(): Filter {
return Filter(filters)
}
}
}
class EVENTS(private val filter: Filter) : Query("events") {
override fun render(builder: StringBuilder, indent: String) {
builder.append("{$name") if (filter.filters.isNotEmpty()) { builder.append("(" + filter.filters.map { if (it.value is Int || it.value is Long) { it.key.str + ":" + it.value + "," } else { it.key.str + ":\"" + it.value + "\"," } }.joinToString(" ").dropLast(1) + ")") } if (children.isNotEmpty()) { builder.append("{\n") for (c in children) { c.render(builder, "$indent ")
}
builder.append("$indent}")
}
builder.append("\n}")
}
fun title() = visitEntity(TITLE())
fun genre() = visitEntity(GENRE())
fun image() = visitEntity(IMAGE())
fun link() = visitEntity(LINK())
fun other() = visitEntity(OTHER())
fun price() = visitEntity(PRICE())
fun text() = visitEntity(TEXT())
fun tickets() = visitEntity(TICKETS())
fun time() = visitEntity(TIME())
fun location(visit: LOCATION.() -> Unit) = visitEntity(LOCATION(), visit)
}
class TITLE : Query("title")
class GENRE : Query("genre")
class IMAGE : Query("image")
class LINK : Query("link")
class OTHER : Query("other")
class PRICE : Query("price")
class TEXT : Query("text")
class TICKETS : Query("tickets")
class TIME : Query("time")
class LOCATION : Query("location") {
fun area() = visitEntity(AREA())
fun place() = visitEntity(PLACE())
fun address(visit: ADDRESS.() -> Unit) = visitEntity(ADDRESS(), visit)
fun coordinates(visit: COORDINATES.() -> Unit) = visitEntity(COORDINATES(), visit)
}
class AREA : Query("area")
class PLACE : Query("place")
class ADDRESS : Query("address") {
fun city() = visitEntity(CITY())
fun street() = visitEntity(STREET())
fun no() = visitEntity(NO())
fun state() = visitEntity(STATE())
fun zip() = visitEntity(ZIP())
}
class CITY : Query("city")
class STREET : Query("street")
class NO : Query("no")
class STATE : Query("state")
class ZIP : Query("zip")
class COORDINATES : Query("coordinates") {
fun longitude() = visitEntity(LONGITUDE())
fun latitude() = visitEntity(LATITUDE())
}
class LONGITUDE : Query("longitude")
class LATITUDE : Query("latitude")
enum class FilterType(val str: String) {
PLACE("place"),
PRICELT("priceLT"),
PRICEGT("priceGT"),
TIMELT("timestampLT"),
TIMEGT("timestampGT"),
AREA("area"),
TITLE("title"),
GENRE("genre")
}
Некоторые классы являются пограничными и не будут располагаться дальше. Поэтому я подумал, можно ли это немного упростить. И если я могу использовать a unaryPlusдля их вызова, когда я вызываю их из main()функции, мне не нужно писать {}после одного крайнего случая, когда у них нет вложенных ожиданий
fun main(){
events(filter) {
title()
genre()
image()
link()
tickets()
other()
price()
text()
time()
location {
area()
place()
address {
city()
street()
no()
state()
zip()
}
coordinates {
longitude()
latitude()
}
}
}
}
Заранее спасибо. Хорошего дня!
Ответы
Да, вы можете использовать unaryPlusвсе, что хотите
Проверьте перегружать документации оператора
Вот вам рабочий пример:
fun main() {
val events = events {
+title
+genre
}
println(events.render())
}
interface Element {
fun render() : String
}
@DslMarker //Domain Specific Language
annotation class GraphQLMarker
@GraphQLMarker
fun events(init: EventBody.() -> Unit): Element {
val events = EventBody()
events.init()
return events
}
class EventBody: Element {
override fun render() =
"events:${elements.joinToString { it.render() }}"
private val elements = mutableListOf<Element>()
operator fun Element.unaryPlus() = elements.add(this)
}
@GraphQLMarker
object title: Element {
override fun render() = "title"
}
@GraphQLMarker
object genre: Element {
override fun render() = "genre"
}
который выводит
events:title, genre