当前位置:网站首页>How to gracefully encapsulate anonymous inner classes (DSL, high-order functions) in kotlin
How to gracefully encapsulate anonymous inner classes (DSL, high-order functions) in kotlin
2022-07-28 15:02:00 【Junerver】
Anonymous inner classes in Java Is a feature often used in , For example, in Android Various in development Listener, It's also easy to use , such as :
//lambda
button.setOnClickListener(v -> {
//do some thing
});
// Anonymous inner class
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//do some thing
}
});
There is only one function interface in Java and Kotlin Can be very convenient to use lambda Expression to abbreviate , But if the interface contains multiple functions , It's easy to use ” Not elegant “ 了 , for example :
etString.addTextChangedListener(object :TextWatcher{
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
TODO("Not yet implemented")
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
TODO("Not yet implemented")
}
override fun afterTextChanged(s: Editable?) {
TODO("Not yet implemented")
}
})
Use with Java Almost , adopt object Keyword implements an anonymous inner class , There's no big problem with this method , In the example above , The three callback functions are not used every time , Many scenarios may only use one or several of them , The rest are empty implementations , It's just not elegant to write such an anonymous inner class every time .
stay Kotlin There are two ways to achieve elegant use of anonymous inner classes :
- DSL
- Higher order function
DSL
DSL The implementation of encapsulation can be divided into the following steps :
1. Create the interface implementation class :XxxxInterfaceDslImpl
And on top of it TextWatcher As an example :
class TextWatcherDslImpl : TextWatcher {
// Corresponding to the original interface kotlin Function object
private var afterTextChanged: ((Editable?) -> Unit)? = null
private var beforeTextChanged: ((CharSequence?, Int, Int, Int) -> Unit)? = null
private var onTextChanged: ((CharSequence?, Int, Int, Int) -> Unit)? = null
/** * DSL Functions used in , Generally, you can keep the same name */
fun afterTextChanged(method: (Editable?) -> Unit) {
afterTextChanged = method
}
fun beforeTextChanged(method: (CharSequence?, Int, Int, Int) -> Unit) {
beforeTextChanged = method
}
fun onTextChanged(method: (CharSequence?, Int, Int, Int) -> Unit) {
onTextChanged = method
}
/** * Functions that implement the original interface */
override fun afterTextChanged(s: Editable?) {
afterTextChanged?.invoke(s)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
beforeTextChanged?.invoke(s, start, count, after)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
onTextChanged?.invoke(s, start, before, count)
}
}
This implementation class consists of three parts :
- Corresponding to the original interface method Kotlin Function object , The signature of the function object is consistent with the corresponding method signature
- DSL function , The name of the function 、 All signatures correspond to the methods of the original interface one by one , Used to receive lambda Assign a value to Kotlin Function object
- Implementation of the original interface method , Implementation of each interface method , It's all about implementing Kotlin Function object call
2. Create an extension function with the same name as the original function , The function parameter is the implementation class extension function
fun TextView.addTextChangedListenerDsl(init: TextWatcherDslImpl.() -> Unit) {
val listener = TextWatcherDslImpl()
listener.init()
this.addTextChangedListener(listener)
}
The extension function has the same name as the original function, which is convenient for users to call , There is no need to remember other function names , If you're worried about confusion , You can add... After the function name Dsl To distinguish . The parameter of this function is the extension function of the implementation class we created in the first step , This is to achieve DSL grammar .
3. Use
etString.addTextChangedListenerDsl {
afterTextChanged {
if (it.toString().length >= 4) {
KeyboardUtils.toggleSoftInput()
}
}
}
When using this method , It can be said to be quite elegant , We just need to call the interface method we need to implement , Interface methods that do not need to be used are null by default .
Higher order function
Higher order function mode is better than DSL The way is simpler :
inline fun TextView.addTextChangedListenerClosure(
crossinline afterTextChanged: (Editable?) -> Unit = {
},
crossinline beforeTextChanged: (CharSequence?, Int, Int, Int) -> Unit = {
charSequence, start, count, after -> },
crossinline onTextChanged: (CharSequence?, Int, Int, Int) -> Unit = {
charSequence, start, after, count -> }
) {
val listener = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
afterTextChanged.invoke(s)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
beforeTextChanged.invoke(s, start, count, after)
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
onTextChanged.invoke(s, start, before, count)
}
}
this.addTextChangedListener(listener)
}
We create an extension function with the same name , Use Closure Tail affix as a distinction , The parameters of this function are one-to-one corresponding to the interface method Kotlin Function object , And assign its default value to {} Namely empty implementation , In the body of the function, pass object Keyword to build an anonymous inner class implementation object , In the implementation of its interface method, it calls one to one. Kotlin Function object .
It is used in the same way as ordinary Kotlin Higher order functions are used in the same way :
etString.addTextChangedListenerClosure(
afterTextChanged = {
if (it.toString().length >= 4) {
KeyboardUtils.toggleSoftInput()
}
},
)
tips:
In the extension function of the above example , We used inline And crossinline Two keywords , This is a Koltin Peculiar .inline Keywords are often used to decorate higher-order functions , Used to improve performance .crossinline Declarative lambda Partial return is not allowed , Used to avoid the wrong use of the caller return Cause the function to interrupt .
Provide a sample code , Try it yourself, maybe you can understand it better :
@Test
fun testInline() {
testClosure {
return
}
}
private inline fun testClosure(test: (String) -> String ) {
println("step 1")
println(test("step test"))
println("step 2")
}
边栏推荐
- 14、 ROS meta function package
- 使用Weka与Excel进行简单的数据分析
- Idea2020.1.4 packages package collapse
- VTK notes - picker picker summary
- Privacy computing summary
- Installing redis in Linux
- Hand in hand from 0 to a "Nuggets special attention" Google plug-in, 5000 words detailed vue3 responsive principle, the advantages, disadvantages and choices of several cache read-write schemes, flyin
- Image steganography method
- How to make the characters in the photos laugh? HMS core video editing service one click smile function makes people smile more naturally
- Four basic data types
猜你喜欢
![[thread safety] what risks may multithreading bring?](/img/79/112ab7e586b0bceb296dfddb2728be.png)
[thread safety] what risks may multithreading bring?

Idea2020.1.4 packages package collapse

使用Weka与Excel进行简单的数据分析

VTK annotation class widget vtkborderwidget

Analysis vulnerability introduction

How to make the characters in the photos laugh? HMS core video editing service one click smile function makes people smile more naturally

Talk about low code / zero code tools

Product Manager

Foundation of knowledge atlas (II) - knowledge expression system of knowledge atlas

Getting started with scottplot tutorial: getting and displaying values at the mouse
随机推荐
C language related programming exercises
RPC (remote procedure call protocol) telecommunication framework
Focus on differentiated product design, intelligent technology efficiency improvement and literacy education around new citizen Finance
6、 C language circular statement
Downloading PIP package is too slow
Install biological sequence de redundancy software CD hit
18、 ROS topic name setting
Crawler: from entry to imprisonment (II) -- Web collector
Solution: attributeerror: type object 'h5py.h5a AttrID has no attribute __ reduce_ cython__
SystemVerilog
SSH service
基于 MinIO 对象存储保障 Rancher 数据
Buuctf partial solution
Matlab load usage
Redis persistence
buuctf_ php
NCBI experience accumulation
3、 C language storage class
Read the introduction tutorial of rainbow
On July 29, apachecon | apachepulsar's exploration and practice in vivo will be broadcast soon