Android Programming In Java - Android Events
Written by Mike James   
Monday, 30 July 2018
Article Index
Android Programming In Java - Android Events
Lambda Types
Closure

Closure

Closure 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:

Button b=(Button) findViewById(R.id.button); b.setOnClickListener( view->{ Button b=((Button) view); b.setText("I've been clicked");

});

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:

Button b=(Button) findViewById(R.id.button); b.setOnClickListener( view->{ b.setText("I've been clicked");});

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:

final Button b=(Button) findViewById(R.id.button);
 b.setOnClickListener( view->{ b.setText("I've been
                                         clicked");
});

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 Class

Final version in book

Using Breakpoints

The 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.

 break

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.break2

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 Event

Final version in book

Anonymous Classes

Final version in book

Code Folding

There 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: 

folding

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.

androidJavaSmall

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 

  • In Java you can't pass a function to set up an event handler you have to pass an object that contains the function as a method.

  • Events are often handled by Single Abstract Method SAM interfaces. The method declared in the SAM is the function that does the event handling.

  • For each event and each control that can generate that event there is a SAM of the form onEventListener and the object has a seteventListener method which can be used to attach the event handler.

  • Java 8 introduced the lambda as a way to make it look as if you are passing a function. A lambda is a short hand way of creating an instance of a SAM with the custom code defined in the lambda.

  • A lambda is the most compact and easy to understand way of creating an event handler.

  • The most direct, and original, way to create an event handler is to create a new class that implements the SAM and then create an instance of the class to handle the event.

  • To avoid the problems with having to create a new class for each event handler Java introduced the anonymous class. You can use an anonymous class to implement the SAM and create an instance of it in one move.

  • You can’t always use a lambda because some events group multiple event handling functions under a single class. In this case the best alternative is to use an anonymous class.

  • Alternatively you can use Activity to implement the interface.

 

androidJavaSmallAndroid Programming In Java:
Starting With an App
Third Edition

Is now available in paperback and ebook.

Available from Amazon.

 

 

  1. Getting Started With Android Studio 3
  2. The Activity And The UI
  3. Building The UI and a Calculator App
  4. Android Events
         Extract: Using Lambdas
  5. Basic Controls
  6. Layout Containers
  7. The ConstraintLayout
        Extract: Guidelines and Barriers
  8. UI Graphics A Deep Dive
        Extract: Programming the UI ***NEW
  9. Menus & The Action Bar
  10. Menus, Context & Popup
  11. Resources
  12. Beginning Bitmap Graphics
        Extract: Simple Animation
  13. Staying Alive! Lifecycle & State
  14. Spinners
  15. Pickers
  16. ListView And Adapters

If you are interested in creating custom template also see:

Custom Projects In Android Studio

Androidgears

 

  • If you want to see how to handle Events using the Kotlin language, see Chapter 4 in Android Programming in Kotlin: Starting With An App

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

espbook

 

Comments




or email your comment to: comments@i-programmer.info

<ASIN:1871962552>

<ASIN:1871962544>

 



Last Updated ( Saturday, 04 August 2018 )