Можно ли вызвать функцию с unaryPlus в kotlin?

Это дополнительный вопрос по другому вопросу, который я задал вчера.

Как создать вложенный список с помощью шаблона построителя?< /а>

Кредит: 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")
}

Некоторые классы являются краевыми случаями и не будут вкладываться ниже. Вот я и подумал, можно ли это немного упростить. И если я могу использовать 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()
            }
        }
    }
}

Заранее спасибо. Хорошего дня!


person mama    schedule 01.12.2020    source источник


Ответы (1)


Да, вы можете использовать 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
person Pelocho    schedule 01.12.2020
comment
Спасибо, но функции по-прежнему запрашивают круглые скобки для вызова. Я обновил свой вопрос для ясности. - person mama; 01.12.2020
comment
Вам всегда понадобится () для вызова функции (или {}, если последним аргументом является лямбда). Так что не делайте этого, не вызывайте функцию. Определите свои вещи как простые object и перегрузите метод unaryPlus - person Pelocho; 01.12.2020
comment
ах да, это может сработать. сейчас попробую - person mama; 01.12.2020
comment
Я пытаюсь использовать val title = object : Query("title") {}, но оно не добавляет имя в построитель запросов. - person mama; 01.12.2020
comment
Это сильно зависит от того, что вы делаете с остальной частью кода. Я попробовал ваш код, и в нем отсутствуют некоторые методы, поэтому я обновил свой ответ фрагментом рабочего кода. - person Pelocho; 01.12.2020
comment
Большое спасибо, это было очень полезно! Мне удалось заставить его работать сейчас, создав класс EdgeCase, который принимает родительский класс в качестве параметра в конструкторе, а затем добавляет себя в список родительских элементов children при инициации. Так что мне даже не нужно больше использовать оператор плюс. Вы можете увидеть мой полный код на github.com/DAT4/gql. -query-builder/blob/master/Main.kt - person mama; 01.12.2020