I heard that… Kotlin can use Lambda?
Java 8 also has Lambda, which is very useful.
I heard that… Can Kotlin’s Lambda still be a function argument?
Ah good good, I will write one!
Ah, error? I change!
Ah?
I…… Change again?
I…… Again…… Change?
Ah !!!!!!!!!!!!
Video first
Kotlin’s higher-order functions, anonymous functions, and Lambda expressions
Watch the video and you don’t have to read the text (but if you like it, just like it).
Kotlin’s higher order function
Hi, I’m Zhu Kai from throwline. Kotlin is convenient, but it can be a pain in the neck at times, and the more convenient it is, like Lambda expressions. A lot of people are attracted to Kotlin because of Lambda, but a lot of people are scared off by Kotlin because of Lambda. In fact, most people who have been using Kotlin for a long time will use Lambda only briefly, and quite a few will not write Lambda at all, relying on the auto-completion features of development tools. I’m going to talk to you today, Lambda. To talk about Lambda, however, we have to start with Kotlin’s higher-order Function.
In Java, if you have an A method that needs to call another B method, you call it from inside;
int a() {
return b(1);
}
a();
Copy the code
If you want to set method b’s parameters dynamically when CALLED by A, you would have to pass the parameters to A and then pass them to B from inside A:
int a(int param) {
return b(param);
}
a(1); // internal call b(1)
a(2); // internal call b(2)
Copy the code
It can all be done, but… What if I want to dynamically set not the method parameters, but the method itself? Such as I have a within a calls to other methods, this method may be b, may be a c, who is not necessarily, I only know that I have a call here, it is int parameter types, the return value type is int, but in a specific performance when the internal call which method, I hope I can dynamic Settings:
int a(??? method) {
return method(1);
}
a(method1);
a(method2);
Copy the code
Or, IF I want to pass a method as an argument to another method, well… Can you do that?
I can’t, I can. Methods are not allowed to be passed as arguments in Java, but we have a time-honored workaround: interfaces. We can wrap methods in the form of interfaces:
public interface Wrapper {
int method(int param);
}
Copy the code
We then take the type of this interface as the parameter type of the external method:
int a(Wrapper wrapper) {
return wrapper.method(1);
}
Copy the code
When calling an external method, pass the object of the interface as an argument:
a(wrapper1);
a(wrapper2);
Copy the code
And if you feel dizzy at this point, let me rewrite it for you:
We will trigger the click event when the user clicks:
// Note: This is simplified code, not the source of the view.java class
public class View { OnClickListener mOnClickListener; .public void onTouchEvent(MotionEvent e) {... mOnClickListener.onClick(this); . }}Copy the code
The core of the click event is to call an onClick() method inside the OnClickListener:
public interface OnClickListener {
void onClick(View v);
}
Copy the code
The OnClickListener is just a shell, with the onClick() method inside. In other words, we pass an OnClickListener:
OnClickListener listener1 = new OnClickListener() {
@Override
void onClick(View v) { doSomething(); }}; view.setOnClickListener(listener1);Copy the code
It is essentially passing a method that can be called later (onClick()). It’s just that Java doesn’t allow passing methods, so we’ve wrapped it in an object to pass it around.
In Kotlin, function arguments can also be of function type:
fun a(funParam: Fun): String {
return funParam(1);
}
Copy the code
When a function has arguments of function type — this is a little convoluted — if you call it, you can — and of course you must — pass it an object of function type;
fun b(param: Int): String {
return param.toString()
}
a(b)
Copy the code
But it’s not as rough as my example.
First of all, the Fun I wrote as a function type is actually wrong. Kotlin doesn’t have a type to indicate that the variable is a function type. This is because a function type is not a single type, but a class type, because function types can have all sorts of different collocations of the types of arguments and return values that belong to different function types. For example, if there are no arguments, no return value (() -> Unit), and if there are single Int arguments, String (Int -> String) are two different types. This makes sense, just as Int and String are two different types. So you can’t just use Fun to say, “This parameter is a function type,” just like you can’t use Class to say, “This parameter is a Class,” because you have to specify what type of function it is, or what the parameters of that function type are, what the parameter types are, what the return values are, You can’t just say, “It’s a function type.”
So for a function type parameter, you specify how many arguments it has, what the type of the argument is, and what the return type is, and you write something like this:
fun a(funParam: (Int) - >String): String {
return funParam(1)}Copy the code
It’s kind of scary to watch. But only by doing so can the caller know what type of function argument to pass to you.
Similarly, function types can be used not only as parameter types of functions, but also as return types of functions:
fun c(param: Int): (Int) - >Unit{... }Copy the code
Such “Functions with arguments or return values of function type” are called “higher-order Functions” in Kotlin.
This so-called “higher order” always gives people a sense of mystery: what is order? Where is it higher? It’s not that complicated, but higher-order functions come from higher-order functions in mathematics. In mathematics, a function is said to be a “higher-order function” if it takes a function as its argument or result. For example, the derivative is a typical example: you take the derivative of f(x) = x, and the result is 1; If I take the derivative of f of x is equal to x squared, I get 2x. Obviously, the function that takes the argument and the result is a function, where the derivative of f of x is one and this is actually a function, but it’s a function that has a constant result of one, so — ah, I’m sorry, but in Kotlin, the argument that has a function type or a function that returns a function type is called a higher-order function, It’s just a name for this class of functions, there’s nothing special about it, there’s nothing special about Kotlin’s higher-order functions, that’s what I’m trying to say.
In addition to being a function parameter and return type, you can assign it to a variable.
For a declared function, however, either you pass it as an argument or you assign it to a variable, always add a double colon to the left of the function name:
a(::b)
val d = ::b
Copy the code
This…… Why?
What exactly is the double colon ::method?
If you go online, you’ll see that double colon is called Function Reference, which is Kotlin’s official term. But what does that mean? That means it points to the function up here, right? So if it’s all the same thing, why not just write the function name instead of adding two colons?
The function becomes an object because of the addition of two colons.
What do you mean?
The essence of the “functions as arguments” thing in Kotlin is that functions can exist as objects in Kotlin — because only objects can be passed as arguments. The same is true for assignment. Only objects can be assigned to variables. But the nature of Kotlin’s function prevents it from being treated as an object. So what to do? Kotlin’s choice is to create an object that has the same functionality as the function. How do I create it? Use double colons.
In Kotlin, a double colon to the left of a function name does not represent the function itself, but represents an object, or a reference to an object, which is not the function itself, but an object that does the same thing as the function.
How do I do the same thing? You can use the double colon object the same way you use the function:
b(1) // Call the function
d(1) // use parentheses after object a to implement the equivalent operation of b()
(::b)(1) // Implement the equivalent of b() with the object :b followed by parentheses
Copy the code
But again, this double colon thing, it’s not a function, it’s an object, an object of type function.
Objects can’t be called with parentheses, right? But objects of function type can. Why is that? Because this is a fake call, it’s Kotlin’s syntactic sugar, and you’re actually calling invoke() on an object of function type by parentheses and arguments:
d(1) // It actually calls d.invoke(1)
(::b)(1) // 实际上会调用 (::b).invoke(1)
Copy the code
So you can call invoke() on an object of type function, but not on a function:
b.invoke(1) / / an error
Copy the code
Why is that? This is because only objects of function type have the invoke() function to use, and functions are not function objects. What type is it? It’s not of any kind. A function is not an object, it has no type, a function is a function, and it has two dimensions with an object.
The double colon plus the function name is a reference to an object, but not to the function itself, but to an object that we can’t see in the code. This object copies the functionality of the original function, but it is not the original function.
This…… It’s the underlying logic, but what do I know about that?
This knowledge will help you solve most of the puzzles of Kotlin’s higher-order functions and the anonymous functions, Lambda related, that I’ll talk about in a second.
Let’s say I have these lines in my code:
fun b(param: Int): String {
return param.toString()
}
val d = ::b
Copy the code
So if I want to assign d to a new variable e:
val e = d
Copy the code
Should I put a double colon or not put a double colon on the d on the right side of my equal sign?
Don’t try, don’t search, just think: this is an assignment, right? The right hand side of the assignment is an object, right? Is d an object? Of course, b is not an object because it comes from the function name, but D is already an object, so I’ll just write it.
Anonymous functions
Let’s move on.
To pass a parameter to a function type, or to assign an object of function type to a variable, instead of using a double colon to retrieve an existing function, you can simply move the function:
a(fun b(param: Int): String {
return param.toString()
});
val d = fun b(param: Int): String {
return param.toString()
}
Copy the code
Also, the function name is useless in this way, so you can omit it:
a(fun(param: Int): String {
return param.toString()
});
val d = fun(param: Int): String {
return param.toString()
}
Copy the code
This is called an anonymous function. Why are they called anonymous functions? It’s easy, because it doesn’t have a name, right? It’s not the name of the function, it’s the name of the variable. The type of this variable is a function type, which in our example code is a function type that takes only one argument, takes an Int, and returns a String.
On the other hand, Kotlin doesn’t allow the left and right names. Since the function on the right doesn’t need a name, Kotlin won’t allow it to have one.
So, if you design a callback in Java like this:
public interface OnClickListener {
void onClick(View v);
}
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
Copy the code
Here’s how it works:
view.setOnClickListener(new OnClickListener() {
@Override
void onClick(View v) { switchToNextPage(); }});Copy the code
In Kotlin, you could say:
fun setOnClickListener(onClick: (View) - >Unit) {
this.onClick = onClick
}
view.setOnClickListener(fun(v: View): Unit) {
switchToNextPage()
})
Copy the code
Make it simple, huh? In most (almost all) cases, anonymous functions can be written more simply as Lambda expressions:
view.setOnClickListener({ v: View ->
switchToNextPage()
})
Copy the code
Lambda expressions
Finally Lambda.
If Lambda is the last argument to a function, you can write Lambda outside the parentheses:
view.setOnClickListener() { v: View ->
switchToNextPage()
}
Copy the code
If Lambda is the only argument to the function, you can also simply remove the parentheses:
view.setOnClickListener { v: View ->
switchToNextPage()
}
Copy the code
In addition, if the Lambda is single-argument, the argument is also omitted:
view.setOnClickListener {
switchToNextPage()
}
Copy the code
Well, if you have a single argument, you don’t have to write it as long as you don’t use it.
Kotlin’s Lambda has a default name for the only argument omitted: it:
view.setOnClickListener {
switchToNextPage()
it.setVisibility(GONE)
}
Copy the code
Kind of cool, huh? But let’s stop for a moment and think: the Lambda doesn’t write either… Isn’t it confused? How does it know its parameter type and return value type?
Infer from the context. Does the function I call have explicit argument information where it is declared?
fun setOnClickListener(onClick: (View) - >Unit) {
this.onClick = onClick
}
Copy the code
The argument type and return value of this parameter are clearly written in here. That’s why Lambda doesn’t have to be written.
So when you want to assign an anonymous function to a variable instead of passing it as a function parameter:
val b = fun(param: Int): String {
return param.toString()
}
Copy the code
If also abbreviated to Lambda form:
val b = { param: Int ->
return param.toString()
}
Copy the code
Lambda argument types cannot be omitted:
val b = {
return it.toString() / / it an error
}
Copy the code
Why is that? It cannot infer the type of the parameter from the context.
If you want to omit argument types here for the sake of the scenario or personal preference, you should specify the type of the variable on the left:
val b: (Int) -> String = {
return it.toString() // it can be inferred to be an Int
}
Copy the code
Lambda returns the last line of code instead of a return:
val b: (Int) -> String = {
it.toString() // it can be inferred to be an Int
}
Copy the code
It’s important to note that Lambda does not return, because if you do, it will use this as the return value of its outer function to terminate the outer function directly. Of course, if that’s what you want to do, that’s fine, but if you just want to return Lambda, that’s a mistake.
And because Lambda is a block of code, it can always infer the return value type from the last line of code, so its return value type really can be left out. In fact, Kotlin’s Lambda cannot write a return value type, so it is syntactically unsupported.
Now let’s pause for a second and think: anonymous functions and Lambda… What exactly are they?
The nature of anonymous functions and Lambda expressions in Kotlin
Let’s look at anonymous functions first. It can be passed as an argument, or it can be assigned to a variable, right?
But we also said that functions can’t be passed as arguments, can’t be assigned to variables, right?
So why are anonymous functions so special?
Because Kotlin’s anonymous functions are not functors. It’s an object. Although Anonymous Function has the word “Function” in its name, including the original Name of Anonymous Function in English, it is actually not a Function, but an object, an object of Function type. It’s the same thing as a double colon plus a function name, and a function is not.
So, you can pass it as a function argument and assign it to a variable directly:
a(fun (param: Int): String {
return param.toString()
});
val a = fun (param: Int): String {
return param.toString()
}
Copy the code
Similarly, Lambda is really just an object of function type. You can use anonymous functions the way you use double colons plus function names, and Lambda expressions.
This is the essence of Kotlin’s anonymous functions and Lambda expressions, both of which are objects of function type. Kotlin’s Lambda is not the same as Java 8’s Lambda, which is just a convenient way of writing, not a functional breakthrough in nature, whereas Kotlin’s Lambda is a real object.
Once you know the essence of Kotlin’s “functions don’t pass, they pass objects” and “anonymous functions and Lambda expressions are really objects,” it will be very easy to write Kotlin’s higher-order functions in the future.
Function Reference = Function Reference = Function Reference = Function Reference = Function Reference But this logic is toxic, and once you believe it, you can’t make sense of anonymous functions and lambdas.
Advertising time:
If you like my video, you can also have a look at my Android advanced serialization course. Add the assistant Diudiu (wechat id: Diuwuxian) and ask her to send you a trial lecture.
White whoring party remember to like and forward, but also for my support.
Compare the Java Lambda
Say a few more things about Java Lambda. Kotlin’s Lambda has a lot of people from Java saying “good to use but bad to write”. It’s a funny thing: you can’t write, so how can you use it? Java introduced Lambda support starting in 8. For Single Abstract Method interfaces — the SAM interface, the Single Abstract Method interface — Java 8 allows you to create anonymous class objects using Lambda expressions. But it’s still essentially creating an anonymous class object, just a shorthand way of writing it, so a Java Lambda is basically written just by code auto-completion. Kotlin lambdas are essentially different from Java, because Kotlin lambdas are real function-type objects with stronger functions, more flexible writing methods, so many people from Java are a little confused.
On the other hand, Kotlin does not support Lambda shorthand for anonymous class objects, since we have arguments of function type, so the single-function interface is unnecessary. Then why are you supporting it?
When interacting with Java, however, Kotlin does support this usage: you can still use lambdas to write arguments to functions that are interfaces to Java’s single abstract methods. It’s not that Kotlin has added functionality. Instead, Kotlin creates an additional bridge method that replaces arguments with function types for interfaces from Java’s single abstract methods, allowing you to create anonymous Java class objects indirectly.
That’s why you’ll notice that when you call setOnClickListener() in the view.java class from Kotlin, you can pass a Lambda to it to create an OnClickListener object, But if you write a Kotlin interface the same way, you can’t pass a Lambda. Because Kotlin expects us to use function-type parameters directly, rather than the interface compromise.
conclusion
Ok, so that’s Kotlin’s higher-order functions, anonymous functions, and Lambda. A quick summary:
-
In Kotlin, there is a class of types that do not exist in Java called “function types”. Objects of this type can be used as functions, but also as parameters of functions, return values of functions, and assigned to variables.
-
There are three ways to create an object of function type: double colon plus function name, anonymous function, and Lambda;
-
Keep in mind that double colons plus function names, anonymous functions, and Lambda are all essentially objects of function type. In Kotlin, anonymous functions are not functions, and Lambda is not a metaphysical “it is just a code block that cannot be categorized.” Kotlin’s Lambda can be categorized, and it is an object of function type.
Of course, there are a lot of details here, you can learn this by yourself, I leave you. Next time we’ll look at Kotlin’s extension properties and extension functions. Keep an eye on me and don’t miss anything new. Bye, everybody
Recommended reading
Kotlin’s variables, functions and types
Kotlin’s generics
Kotlin coroutine hanging is so mysterious and difficult to understand? I took the skin off him today
What exactly is a “non-blocking” suspension? Are coroutines really lighter?
[Throwline] Disappeared for six months, where have I been