Mastering the Kotlin DSL in Android

foreword

In this article, we'll learn how to write a Kotlin DSL in your Android project.

This article is going to be long, so take a moment and let's write your DSL together. We will discuss the following topics,

  • What is a DSL in plain English?
  • Do you use any DSL?
  • Why do we use DSLs?
  • How we write our own DSL
  • Basic example instructions.

So let's get started.

What are DSLs?

Wikipedia's definition says,

A Domain Specific Language (DSL) is a computer language that is specialized for a specific application domain. This is in contrast to the General Purpose Language (GPL), which is broadly applicable across domains.

Do you like to be serious?

In general, a DSL gives you any language-specific flexible tools to take advantage of the features provided by a particular programming language.

Do you use any DSL?

If you are an android developer and use kotlin in your project, you may be using a DSL in your project intentionally or not? Can you think of an example?

Let me help you, have you ever written code like this,

yourlist.forEach { 
    //your code is here
}

The above example is using forEach in a list in Kotlin. forEach is kotlin is an example of a DSL

Why do we use DSLs?

We should use DSL to simplify and increase the complexity of the application and make the code more readable.

How can we write our own DSL?

Before we start writing our own DSL, we need to understand lambdas with receivers.
But also let me give a brief overview of lambda with receiver

Suppose we have a function buildString,

fun buildString(action: (StringBuilder).() -> Unit): String {
    val stringBuilder = StringBuilder()
    action(stringBuilder)
    return stringBuilder.toString()
}

Here we have a function buildString which takes the action (which is a function) as a parameter. The action function here takes StringBuilder as a parameter, and the function buildString takes String as its return type.

Now, to use the buildString we wrote ,

buildString {
    append("<")
    append("MindOrks")
    append(">")
}

We're using Kotlin's properties here to pass StringBuilder-related properties because we're using the power of extension functions in the buildString function.

We can also create a custom DSL using,

infix

In Kotlin, infixes help us create custom DSLs, similar to how we write them in English. For example,

In English we say "1 plus 2" to sum or difference, we say "1 minus 2". Similar things can be achieved in Kotin using infix calls.

To create an infix to add the numbers we use,

infix fun Int.plus(number: Int) = this + number

Here we create an extension function for Int, plus it takes a number and returns the sum of this + number. This is the number of applied functions.

So, to use this infix function we use,

val output = 1 plus 2

In the code above, we use the infix function where we create the plus sign to generate the output. When we print it in Logcat, it prints 3 as the sum.

This is because we created an infix expansion function to use the plus sign literal instead of " + ", which we use as the traditional way.

Likewise, if we wanted to make the infix function minus, we use

infix fun Int.minus(number: Int) = this - number

To use it, we use,

val output = 1 minus 2

Infixes make the code readable and well organized, anyone can read it. So if anyone who knows nothing about programming can also say this is adding or subtracting two numbers.

transfer

In call, the operator allows any object to be called as a function. Here, let us create a class called Student ,

class Student {
    operator fun invoke(student: Student.() -> Unit) = student
    fun addName(name: String) {
        //implementation
    }
    fun addMarks(marks: Int) {
        //implementation
    }
}

In the class, we create a function call that takes a parameter of type student and returns the student itself. So, here we can use all methods of the Student class in the object of the student itself.

Now, to use Class, we still create objects like in Kotlin,

val student = Student()

Now you can see that we have another function called addName and addMarks ** We will use this as a DSL. However, here we can use it in two different ways. Both are as follows,

  1. Type - 1 (traditional way)
student.addName("MindOrks")
student.addMarks(100)

The above code is like the old traditional way we used to do in Android. 2.DSL mode

student {
    addName("MindOrks")
    addMarks(100)
}

Here, we use the Kotlin DSL. If you noticed, we have lambdas like,

Student.() -> Unit

If you noticed, we have a .() in there . It specifies that the lambda has a receiver, and to use it we need to create a constructor of the class. This is how you can create a DSL using calls.

In the above way, you can create your winning DSL in Kotlin.

Now, let's discuss use cases and examples of DSLs in Android.

  1. data class

Here, let's discuss how to create a DSL for data classes. Consider we have a data class Student

data class Student(
    var name: String? = null,
    var age: Int? = null,
    var marks: Int? = null
)

Here, if we use the data class we wrote,

val student =  Student(
      "MindOrks",
      20,
      30
  )

Now to transform the above code in DSL,

Now we will create a new lambda as follows,

fun student(student: Student.() -> Unit): Student = Student().apply(student)

In the code above,

We have a receiver with parameter student and use it as a DSL that we use,

val student = student {
    name = "MindOrks"
    age = 20
    marks = 30
}

This is how we can convert our data classes into a DSL.

  1. UI elements

For this, let's treat textView as a UI element. In that, we use as follows,

textView.text = "MindOrks"
textView.setOnClickListener {  
    
} 
textView.setTextColor(Color.BLACK)

But to consume it through the DSL we use,

textView.apply {
    text = "MindOrks"
    setOnClickListener {

    }
    textColor(Color.BLACK)
}

We use apply to create a DSL in any UI element .

  1. JSON

To create JSON in Android we use,

val jsonObject = JSONObject()
jsonObject.put("name","MindOrks")
jsonObject.put("age",20)

This is the traditional way of creating JSON objects.

Now, let's see how to create a DSL to create JSON. First, we'll create a class and extend it with JSONObject(),

class Json() : JSONObject() {

}

Now, we will use the lambda constructor with receiver

constructor(json: Json.() -> Unit) : this() {
    this.init()
}

A generic will be created using infix to add values ​​to the JSON object.

look like,

infix fun <T> String.to(value: T) {
    put(this, value)
}

Here, it will put the value with the string key and the value will use the type of "to".

Now, the complete class file looks like,

class Json() : JSONObject() {

    constructor(json: Json.() -> Unit) : this() {
        this.json()
    }

    infix fun <T> String.to(value: T) {
        put(this, value)
    }
}
为了在 Activity 文件中使用它,我们使用刚刚创建的 DSL 创建 JSON,

val json = Json {
    "name" to "MindOrks"
    "age" to 20
}

here,

  • to is the infix we created to put the value into the JSON object.
  • JSON is a class we created that takes keys and values ​​to create JSON objects.

When we print it in Logcat, we get the following output,

{"name":"MindOrks","age":20}

This is how you can create DSL in kotlin code.

Guess you like

Origin blog.csdn.net/qq_39312146/article/details/131353631