Est-il possible d'appeler une fonction avec un unaryPlus dans kotlin?
C'est une question complémentaire à une autre question que j'ai posée hier.
Comment puis-je créer une liste imbriquée à l'aide d'un modèle de générateur?
Merci à: Pelocho pour sa belle réponse.
J'ai utilisé ce didacticiel pour créer un générateur de requêtes de type GraphQL sécurisé:
Ce que je veux faire maintenant, c'est simplifier ce que j'ai fait Et je sais que kotlin doit avoir quelques fonctionnalités intéressantes pour le faire.
Pour le moment, je dois appeler des fonctions lorsque je veux ajouter une entité à ma requête:
fun main() {
events{
title() // I don't like to do () when it is an edge case
}
}
Je me demande s'il est possible de faire quelque chose de différent, et de plus simple, comme:
fun main() {
events{
title // this is much nicer
}
}
Mais si ce n'est pas possible, il est juste de réduire la quantité de caractères avec une surcharge d'opérateur comme par exemple.unaryPlus
fun main() {
events{
+title // this is also nicer
}
}
Mon code que j'ai maintenant a une classe appelée Query
où j'ai mis la méthode:
operator fun Query.unaryPlus() {
visitEntity(this,{})
}
Mais cela ne semble pas fonctionner pour moi.
Tout mon code est ici.
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()
}
}
Ensuite, j'ai créé les classes pertinentes pour mon cas
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")
}
Certaines classes sont des boîtiers de bord et ne s'emboîtent pas plus bas. Alors je me demandais s'il était possible de simplifier un peu cela. Et si je peux utiliser a unaryPlus
pour les invoquer lorsque je les appelle depuis la main()
fonction, je n'ai donc pas à écrire {}
après un seul cas de contour, quand ils n'ont pas d'imbrication
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()
}
}
}
}
Merci d'avance. Bonne journée!
Réponses
Oui, vous pouvez utiliser unaryPlus
ce que vous voulez
Consultez la documentation de surcharge de l' opérateur
Voici un exemple de travail:
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"
}
quelles sorties
events:title, genre