Thumbnail for Functional Interface in Java 8 Explained | Lambda Expressions, @FunctionalInterface, Anonymous Class by The Curious Coder

Functional Interface in Java 8 Explained | Lambda Expressions, @FunctionalInterface, Anonymous Class

The Curious Coder

24m 31s4,029 words~21 min read
Auto-Generated

[0:00]Hello and welcome to my channel, The Curious Coder. In this video, we'll be learning about one of the most important feature that Java introduced, functional interface. What is a functional interface? It is an interface that contains exactly one abstract unimplemented method making it suitable for use with lambda expressions. Alright. So it's an interface that contains exactly one method which is abstract, not more, not less.

[0:33]Why? Because that makes it suitable for lambda expressions. Now what are lambda expressions? That we'll understand just in a while. For now, let's try to create a functional interface. So what I'm going to do is I'll just simply create an interface, let's say my interface. Now, is this a functional interface? No, not yet because as of now, it does not have any abstract method, but we need one abstract method. All right. So let me add a method. Let's say void method one. All right. So now is it a functional interface? Yes, now it is because it has only one abstract method. Okay. So I hope you guys know what abstract methods are. Basically, these are the methods that we just declare. They don't have any body, okay, they don't have any body like this. So these are called abstract methods. All right. So this can be considered as a functional interface. Now let's say I add one more method, void method two, like this. So now is is this a functional interface? No. Again, this is not a functional interface because it has two abstract methods and we only need one. Okay. But let's say I make it a default method like this. All right. So default methods have body inside them, okay, we can add curly braces and give any body if we want. So now is this a functional interface? Yes, it is still a functional interface because the only rule is that there should be one abstract method. But we can have as many default methods as we want. Okay. Similarly, we can have static methods inside a functional interface. Okay. So let's say we have a method three, like this. So still this can be considered as a functional interface. All right. Also, there is one more thing. It is recommended that we should put this annotation @FunctionalInterface on a functional interface. Okay. So what is the role of this? It basically restricts the interface to behave as a functional interface. All right. What do I mean by that? Let's say now if I add one more method, okay, like this, void method four. So I've added one more abstract method. So we'll see that it is giving me some compilation issue. It says multiple non-overriding abstract methods found in my interface. All right. So this means that this annotation has added a restriction on this interface that it should not break the rules of functional interface. All right. And as I told you, functional interface can only have one abstract method. So that's why it's giving me a compilation issue because here we have created two. Okay. So I'll just remove this and now it works fine. Similarly, if I'll just comment this one, again it will give me any, it it will give me error that says no target method found because we still need one method. Okay. So this is what functional interface is. All right, these are all the rules and this is how we can create any functional interface. Okay. Now what's the purpose of it? Why does it have this rule that it should have only one method and what exactly we are going to do with this interface? So actually, the main purpose behind this is that we can we can give an an implementation of this interface using lambda expressions. All right. So now let's understand what lambda expressions are. Lambda expressions are a concise way to represent an implementation of a functional interface using a short, readable syntax. Basically, they are designed to reduce a lot of boiler plate code. All right. So they implement the functional interface and in a very short manner, okay, using very less syntax. All right. So let's see how how they do that. So I have created an interface named Vehicle. And I've declared it as a functional interface. And it has this abstract method start. Okay. Now, let's say I want to call this method. Okay. So, can I just create an object of vehicle like this? And using this, can I call it? Okay, we have vehicle v1 is equal to new vehicle and using this I want to call this method. Okay. So it gives me some compilation issue that says, Vehicle is abstract, cannot be instantiated. Okay. So there is a problem with this and the problem is that we are actually not allowed to create objects of interfaces. Okay. Now, why?

[5:06]Why can't we do that? Because see, logically, this start method does not have any implementation. There is no body of this start method. So if you will create an object of vehicle and if you are going to call this method, then what exactly should be called? Right? Because we don't have any body. We don't have any implementation. Okay. So the compiler won't know what exactly to trigger. Okay. So what's the solution? The solution is that we can create a child class to implement this vehicle. Okay. So let's say I have a class named car. That implements vehicle. Okay. And here we will have to implement this method. All right, so this car class will override this method vehicle and give some implementation. Okay. So let's say I'll say System.out.println. I will say car engine started.

[6:01]All right. So now we have an implementation for this method. Okay. But still, if I'm trying to call it using the vehicle object, it still gives me an error. Now what's the problem? So the problem is that even if we create an implementation, but still we cannot call it using the vehicles object. Because the thing is that what if I create more implementations? Okay, let's say I have another class bike, which also implements vehicle. Okay. And here let's say I give a different implementation. Okay, I'll just say bike engine started. All right. So now how would the compiler know that when we are calling it using a vehicle object, which of these two implementations should the compiler call? Okay, we have not specified that. That is why we cannot create an object like this. Okay. So what was the solution? Solution was that we should create an object directly of the child class. Okay. So the child class, one of the child class is car. So I'll create an object of car and put it in a reference of vehicle. And using this, I can call the start method. Similarly, I can also do the same for bike. Okay, I can create an object of bike. And using this, I can also call the start method. Okay, so if I run it. Great. You will see that this printed car engine started and this one printed bike engine started. All right. Now this is how things were done before Java 8. Now, you might observe that whatever we did so far is actually containing a lot of redundant code. Okay, there is a lot of boiler plate code that we actually don't need. Why? Because you see, we have this start method and at the end of the day, we only want to implement it. But just to do that, we are creating two child classes and then we are calling methods using those objects. Okay, so I think there is a scope of reducing this code. All right. So what can I do? I can just I'll let me just first comment this out. Okay. And one second, I'll just copy-paste this. I'll copy this, paste it over here. Okay. So I'll comment this part. This was one way of doing things. Now we'll look at the second method. Okay. So at the end of the day, all I need is I need some implementation here. Okay. So instead of giving it like this, like writing it like this, new car, I'll just simply write new vehicle. Okay. And if somehow I give I provide an implementation here itself instead of creating this class, then my job is done. Okay. So how can I do that? I'll just simply click here, implement methods. And this is how I will provide an implementation here itself. This is called anonymous inner class. All right. So we didn't need a separate car class. We can provide an anonymous implementation here itself. And here I can just simply say System.out.println. Let's say I want to give an implementation of car only, so I say car engine started. Okay. So now I won't be needing this class car. Okay, I can delete it. Okay. And using this V1, now I can call v1.start. All right. Similarly, I'll do the same for bike. I can just say vehicle.

[9:30]And here I will implement. Okay. So System.out.println. Let's say bike engine started. All right. So v1.start, v2.start. Let me just call this. Great. So again, using anonymous inner classes, we can implement these, we can implement this method. Okay. And now I'll also delete the class bike. Okay. So now lambda what lambda expressions do is that they they are actually one step further. Okay, they reduce this code even more. That is what lambda expression is. It is nothing fancy. It is just a way to give implementation of a method. All right inside an inside a functional interface. Okay. So let me just copy this also and I'll comment this part. This is so this was the first way, this is the second way of doing things. Now, I'll show you the third way. Okay. So see, the catch here is that this vehicle interface contains only one abstract method. Because it's a functional interface. All right, that we know. And actually that compiler also knows. All right. So that is why, since it's only one method, so do we really need to provide all this name over here that we are implementing this method start? We don't need to tell the compiler again. It already knows that this interface has method start. So whichever implementation I'm giving here, is of will obviously of the method start. Okay. So I don't need this code. So I won't be needing this. And actually, I already know that it's a vehicle reference, so I don't even need to write this. Okay. So I'll just remove this as well. All right. That's it. This is the implementation. And when I'm giving it like this, I have to add a hyphen and then an arrow. This is what lambda represents. This is called lambda, and this is my final syntax. All right. Very, very simple. It reduces the code to a very, very large extent. You'll see that we have given these parentheses then a lambda. And here I'm giving the implementation. So this actually represents this only. Okay, the way we gave an implementation here. Similarly, I can write it in a short way like this. And this is what called a lambda expression. And you will see that in this method implementation, we are only writing one statement, a single statement. So, in that case, I don't even need these inside braces. Okay. So I'll just remove this and I'll remove one semicolon and I'll remove this. All right. So this is how I can implement using lambda. Similarly, I'll do it again. I'll show it to you. First, we remove this method name and override. We remove this parenthesis. We don't need to tell the compiler that it's a vehicle object because we have the vehicle reference. Okay. So we remove this. Here I will add a hyphen, then an arrow. And this is the implementation. Now this implementation has only one line. So I don't even need this. And I don't need this. Okay. Simple. This is how I will give my implementation. All right. So you will see that we have done all this only in just a single line. Okay. Now again, I want to tell you what it actually represents. This line here represents the implementation of the start method. Okay. So whatever the start method, whatever implementation, whatever body you want to provide to this start method, that is written here after the lambda. Okay. Similarly here in V2 and I'll run it again. Great. So again, car engine started and bike engine started. Okay. So the first way is that you create a child class and using the object of that class, you call the method. The second way is that you provide an implementation using an anonymous inner class. And the third way is that you do it using a lambda function. Okay, lambda expression. So this is just a different type of syntax, nothing else. It's very short, and it only works because vehicle has one abstract method. All right. If it would have had multiple abstract methods, then we could not do it like this because then the compiler won't know that which method we are talking about. Because then we will have to name the method like we named it here. Okay. But since there is only one abstract method, so that's why we can give an implementation like this. All right. Now, I think that this topic is a little hard to understand, especially in the beginning. So to understand it, there is only one way that we should practice it a lot. And that's why in my video, I'll be taking multiple examples. I've created four different functional interfaces, and one by one I'll be taking, I'll be implementing all these interfaces. But still my recommendation is that you should practice it on your own as well. Okay. Create some functional interfaces, do some implementations of it, read some examples, and eventually you'll get a hold of it. All right. But in my video as well, I'll cover a lot of examples and I hope that it will be very helpful. Okay. So this is the first interface. This is an interface named greeting and it has this abstract method. Okay. So I'll implement it the same way. I've implemented the vehicle interface. Okay. Now the trick is that always first write the interface name. All right. Because see, every time you won't do it like this. You won't be creating a child class first, then you will be creating an anonymous class and then breaking down that code. Okay, you should directly write a lambda function. Okay, at some point, you should know how to write it directly. Okay. So I'll just tell you the trick for that. So first write the interface name, then give your object some name, let's say I say G1, and after that, just check how many arguments does this method have. So as of now, it does not have any argument. So simply, just add a parenthesis, add the lambda, the hyphen and an arrow, and always keep make it a habit. Always first give these curly braces in the beginning. Okay. Always first give this curly braces and a semicolon. Write it like this. Now this is the area where you're going to give your implementation. Okay. This is where this method greet will be implemented. All right. So let's say I simply write a statement, System.out.print. Hello everyone. Okay. A simple print statement. And that's it. You have given an implementation for the greet method. Now after that, check that whether this implementation is a single line implementation. Yes. In this case, it is a single line, we are only printing something. So, in that case, you can remove this. You can remove this, and you can remove this semicolon. Okay. So this is the whole implementation. All right. Now using this G1, you can call the greet method. All right. Now similarly, let's take one more example. We have an interface named task and the method is execute. Okay. So this time, again, I'll take task. Let's say I name the object T1. Is equal to in this task, again, we don't have any argument. So I'll simply take a parenthesis, then a lambda. And again, initially, I will add these curly braces and a semicolon. Now let's say I want to write two statements here. Okay. First is that task started, then something like let's say task ended. Okay. Simple. So in this case, I cannot remove these braces. Okay. If I want both these statements to be a part of the implementation of this execute, then I will have to write both these statements inside the curly braces. All right. That's the rule. So this is how you implement a multi-line implementation, you give a multi-line implementation, but in case of a single line, you can remove these braces as well as this semicolon. Okay. Again, using this T1, I can call T1.execute. All right. Cool. Now coming to the third example. This is an interface square and let me just also add this annotation. Okay. Even though if I didn't add it, it would have worked because there is only a single method, but I still still it's recommended, so I like to add it. All right. So we have this interface. It has this method operate. Now this time, the return type is int and we are also taking an argument. All right. Now that is an interesting example. So first, always write the interface name. Let's say square. S1 is equal to this time, we have an argument. Okay, we have one argument. So here I will take an argument. All right, like this, number. And I'll write the lambda and then the curly braces. Okay. So we will have to take an argument because here in the method in the method square, we have this argument. Okay. Now this number using this number, I'm saying return number X number. Okay, I'm returning the square and put a semicolon here as well. All right. So now this is the implementation. Okay. So is there any scope of reducing this further? Actually, yes, there is. First, let me just clarify one more point. I'll reduce it as well. But let me just query one more point. So the thing, the thing is when you see the code like this, many people get confused. But the thing is that since we are returning something here, let's say the number is 10. Okay. So we are returning something here. So people think that the value 100, that will be returned and will be saved in this object. But that is not true. The return type, the returning thing is of this method, which is this square, which is this operate. Okay. So when you're actually calling the operate method, at that point of time, this code will be triggered. Okay. So whatever you are returning here, that is not getting returned here in S1, that will be returned here. I'll just print it. System.out.println. Okay. So when you actually call the method, at that point of time, this implementation will be called. All right. This is how it is working. Imagine it like this, at this point, you have only given the implementation. You have not actually triggered any code because the method is actually getting hit at this point of time, when you're actually calling the operate method. All right. So just please think of it like this. I wanted to make it clear. Okay. Anyways. So in the operate method, here I will pass the value, which is 10. This value, this 10 will be passed on here and it will actually return 10 into 10, so 100. Okay. So this will be returned here in the print statement. All right. Now okay. Can I further optimize it? Yes. How? First, this has, this also has only one line. Correct. So I can remove this. I can remove this, and I can remove this. All right. Now there is one rule. If inside this single line, there is a return statement and you remove the curly braces, then you also have to remove this return. Okay. Compiler already knows that you are returning something. So the implementation will be like this. There is a number and you are returning a square of number. Now this is just an implementation. This will actually be triggered when you'll call the operate method, which is here. Okay. And there is one more optimization. If there is a single parameter, in that case, you're also allowed to remove these parenthesis. Okay. So please remember that if there is a single parameter, you can also remove this parenthesis. So you will just write number and return number into number. All right. Great. Let me just run it so far so that you can see. Okay. So when G1.greet was called, this statement was triggered, and hello everyone was printed. When T1.execute was called, these two statements were triggered, and task started, task ended was printed. Similarly, when S1.operate was called, then only this square was returned in this from this operate method. And whatever value was returned, we are printing it. That's why 100 was printed. Okay. Cool. Now let me take one more example. This time, we have two arguments. Okay. And we are returning some int value. All right. So I'll take this example. I'll write the interface name, calculator, give object some name, let's say C1, is equal to since there are two arguments, so I will have to take it like this. Let's say A, B. Okay, you can name anything, you can name the argument anything. Like here it's num1 and num2, but here you can say a, b as well. There is no issue in that. All right. Now an a hyphen and an arrow representing lambda. Then these parenthesis and this semicolon. Okay. So I could just directly return A + B. But let's say I'm taking it like this, int result is equal to A + B. And I'm also printing it. Let's say result. Okay. Which is this result. And then I'm also returning this result. All right, like this. So this implementation is not a single line implementation. That's why I should not remove these curly braces. And has this code been called yet? No. Because as of now, we have only provided the implementation, which is there in the C1. But when I'll call it using C1, c1.add, let's say 10, 20. At this point of time, this code will be triggered. Okay. And since I have this print statement here only, so I will not print it again. Let me just show you. Great. So this statement got triggered. Result is 30. Okay, the sum of A and B. So you passed 10 and 20 in this add method. So these were 10 was equal to A, 20 was equal to B. These were saved here in the result. And then it was printed here and also returned back. So where is it returned? It will be returned here. Okay, I can add it here as well. If I'll print this, this will also be equal to 30. All right. So, this is what a functional interface is. This is what a lambda expression is. I I hope this video was helpful. My recommendation is, please practice it on your own as well. I'll you'll definitely get a hold of it. Use this trick that I told you. First, write the interface name, then some object, then count the parenthesis, the number of arguments, then lambda, then provide this curly braces. And between these curly braces, give your implementation that you want to give. All right. So for more such content, please follow my channel, The Curious Coder.

Need another transcript?

Paste any YouTube URL to get a clean transcript in seconds.

Get a Transcript