Android Programming In Java - Android Events |
Written by Mike James | ||||
Monday, 30 July 2018 | ||||
Page 3 of 3
ClosureClosure is one of those topics that sounds as if it is going to be difficult. Lambda expressions are different from most functions in that they have access to all of the variables that belong to the method that they are declared in. What is more a lambda cannot declare a variable that already exists in the enclosing method – that is a lambda cannot create a shadow variable for a variable that it can already access. For example:
this looks as if it should just work because all that we have done is to change the previous example so that the view parameter is cast a Button and stored in a new variable b. However if you try this out you will discover that the compiler complains that you cannot declare variable b because it already exists. The lambda has access to the b set in the enclosing method and it cannot create a shadowing variable of the same name. The fact that the lambda has access to the variables in the enclosing method has the strange consequence that you could write the event handler as:
This may look odd but it works. If you don’t think that it is odd then you haven’t noticed that the event handler, the lambda, will be executed when the Button is clicked and this is likely to be well after the enclosing method has finished and all its variables not longer exist – and yet the lambda can still use b to access the Button. The system will keep the value of b so that the lambda can make use of it. This is the essence of a closure – the preserving of variables that have gone out of scope so that a lambda can still access them. Now we come to a subtle point that you might want to skip until you are sure you understand closure or see an error message relating to “final”. The Java closure isn’t as general as you will find in other languages. It captures the values of the variables just once at the time that the lambda is created. To make sure that strange things cannot happen as a consequence of this you can only use variables with are final or effectively final in the lambda. A final variable is one that can be initialized and then is guaranteed not to change. That is a final variable is one that is set to a value just once. You can use the final modifier to make it clear that a variable satisfies this condition and the compiler will check that it is true. So for example you could write the previous event handler as:
The final modifier would cause the compiler to check that b was initialized just once. In fact you don’t need the final modifier because using b in the lambda causes the compiler to check that it is effectively final. It is quite clear the b is only initialized once in the code listed above and so it seems that it is self evidently final but things are more subtle. Suppose the enclosing method was called a second time? Surely this will change the value in b and hence it isn’t final. As b is a local variable it is a new instance of b that is initialized and a new instance of the lambda is created. As the original b isn’t reinitialized it is still final. It can be very difficult sometimes to work out if a variable is final or not but the compiler will do it for you. Event Handler Using A ClassFinal version in book Using BreakpointsThe easiest way to check that the event handler is called is to insert a breakpoint on the first line of the onClick method. Breakpoints are a fundamental debugging tool and you need to learn to use them as early as possible. To place a breakpoint simply click in the "margin" next to the line of code. A red blob appears: Now when you run the program using the Debug icon or the Run, Debug command the program will stop when it reaches any line of code with a breakpoint set.
Once the program pauses you can see where it has reached in its execution and you can examine what is stored in all of the variables in use. You can also restart the program or step through it – see the icons at the top of the debug window. As you step though you will see the values in the variables change. Any bug will be found at the first place you find a discrepancy between what you expect to find and what you actually find. There is much more to learn about debugging but for now this small introduction is enough to save you a lot of time. Alternative Ways to Handle an EventFinal version in book Anonymous ClassesFinal version in book Code FoldingThere is one small mystery that we have to deal with. Android Studio uses code folding to hide the details of blocks of code from you. Usually this is useful, but in the case of lambdas and anonymous classes it can be confusing. For example if you type in the code above that sets the onClickListener then in folded view the code reads: This is exactly how you would write the same code using a lambda. Indeed lambdas are converted to this form by the compiler i.e. they are syntactic sugar on top of anonymous classes. But this way of representing the code is purely for looking at. If you click the small + button to the left you will see the code expand to reveal the full version with the anonymous class. So be careful not everything that looks like a Java 8 lambda is actually a Java 8 lambda, it could be an anonymous class that the code editor just decides to show you as if it was a lambda. The only time this could cause a problem is if you were working with a pre-Java 8 program and mistakenly believed that you could use lambdas. Which Approach Should You Use?Lambdas are equivalent to anonymous classes and you can use whichever you prefer but if you can use Java 8 in your project I would recommend writing event handlers as lambdas where possible – they are simply easier to read. Of course if the event handler isn’t implemented as a SAM then you cannot use a lambda. There are lots of examples of event handlers being grouped together under the control of a single object. This was done to make it easier to implement in the days before lambdas but now it simply means you have to use an alternative method such as an anonymous class. There seems to be little reason to use the full “implement a new class” approach. The only possible reason is if you need to instantiate multiple copies of the event handler and this isn’t usual. Implementing event handlers are part of the class that generates the events still has some advantages but you cannot provide multiple event handlers for and event on different instances of a control. The short version is – if you can use a lambda and if you can’t use an anonymous class. This is what all of the following examples will do.
Summary
|
||||
Last Updated ( Saturday, 04 August 2018 ) |