Java for Android Development: Inner Classes

What is an Inner Class?

Most classes in Java are top-level classes. These classes, and the objects they define, are stand-alone. You can also create nested classes in order to clearly encapsulate and define subordinate objects that only matter in the context of the outer class. Nested classes are called inner classes.
Inner classes can have all the features of a regular class, but their scope is limited. Inner classes have another benefit: they have full access to the class in which they are nested—this feature makes inner classes perfect for implementing adapter functionality like iterators.
Here’s an example of a top-level class with two inner classes:
 
  1. public class User {   
  2.   
  3.     //  User fields, including variables of type LoginInfo and UserPreferences    
  •     // Misc user methods   
  •        
  •     class LoginInfo   
  •     {   
  •         // Login info fields   
  •         // Login/Logout methods   
  •         // Can access User fields/methods   
  •     }   
  •        
  •     class Preferences   
  •     {   
  •         // User preference fields   
  •         // Get/Set preference methods   
  •         // Reset preferences method   
  •         // Can access User fields/methods   
  •     }   
  • }  
  • In this example, the User class has two inner classes: LoginInfo and Preferences. While all user-related data and functionality could be defined in the User class, using the inner classes to compartmentalize functionality can make code easier to read and maintain. The inner classes LoginInfo and Preferences also have access to the protected/private fields and methods available within the User class, which they might not otherwise have due to security, if they were defined as stand-alone classes themselves.
    It’s important to remember, though, that inner classes really only exist to help the developer organize code; the compiler treats inner classes just like any other class, except that the inner classes have a limited scope, and are therefore tethered to the class they are defined with. Said another way, you would not be able to use or instantiate the LoginInfo or Preferences classes except with an instance of the User class, but the inner classes could access any fields or methods available in the outer class User, as needed.

    Using Static Nested Classes

    One particularly use for nested classes is static nested classes. A static inner class defines behavior that is not tied to a specific object instance, but applies across all instances. For example, we could add a third nested class, this time static, to the User class to control server-related functionality:
     
    public class User {   
    1.   
    2.     //  User fields, including variables of type LoginInfo and UserPreferences    
  •     // Misc user methods   
  •        
  •     class LoginInfo {}   
  •        
  •     public static class ServerInfo {}   
  •     {   
  •         // Server info applies to all instances of User   
  •     }   
  • }  
  • Because it’s public, this static nested class can be instantiated using the following new statement:
     
    User.ServerInfo sInfo = new User.ServerInfo();  
    The outer class does not have to be instantiated to perform this instantiation, thus the use of the class name. As such, a static nested class, unlike a non-static nested class (aka inner class) does not have access to members of the outer class–they may not even be instantiated

    The Power of Anonymous Inner Classes

    Android uses anonymous inner classes to great effect. Anonymous inner classes are basically developer shorthand, allowing the developer to create, define, and use a custom object all in one “line.” You may have seen examples of the use of anonymous inner class in sample code and not even realized it.
    To create an anonymous inner class, you only provide the right-hand side of the definition. Begin with the new keyword, followed by the class or interface you wish to extend or implement, followed by the class definition. This will create the class and return it as a value which you can then use to call a method.
    When we use an anonymous inner class, the object created does not get assigned a name (thus the term anonymous). The side effect, of course, is that the object is used used just once. For example, it’s common to use an anonymous inner class to construct a custom version of an object as a return value. For example, here we extend the Truck class (assuming its defined elsewhere and has a field called mpg and two methods, start() and stop():
     
    Truck getTruck()   
    1. {   
    2.     return new Truck()    
  •     {   
  •         int mpg = 3;    
  •         void start() { /* start implementation */ }   
  •         void stop() { /* stop implementation */ }   
  •     };   
  • }  
  • Now let’s look at practical examples of anonymous inner classes used in Android.

    Using an Anonymous Inner Class to Define a Listener

    Android developers often use anonymous inner classes to define specialized listeners, which register callbacks for specific behavior when an event occurs. For example, to listen for clicks on a View control, the developer must call the setOnClickListener() method, which takes a single parameter: a View.OnClickListener object.
    Developers routinely use the anonymous inner class technique to create, define and use their custom View.OnClickListener, as follows:
     
    Button aButton = (Button) findViewById(R.id.MyButton);   
    1. aButton.setOnClickListener(new View.OnClickListener() {   
  •             public void onClick(View v) {   
  •                 // User clicked my button, do something here!   
  •             }   
  • });  
  • Using an Anonymous Inner Class to Start a Thread

    Let’s look at another example. It’s quite common to define a new Thread class, provide the implementation of its run() method, and start that thread, all in one go:
     
    new Thread() {    
    1.     public void run()   
  •     {   
  •         doWorkHere();   
  •     }   
  • }.start();  
  • Using a Named Inner Class

    Using anonymous inner classes for listeners in Android is so common that it is practically second nature to do so. Why, then, would you not want to use them? Let’s answer this through a hypothetical example.
    Let’s say you have a screen that has 100 buttons on it (we did say hypothetical, right?). Now, let’s say each button, when pressed, does the exact same thing. In this case, we’ll just listen for clicks and Toast the text from the View object passed in (the text shown on the Button that was clicked):
    Here’s pseudo code to do that:
     
    Button[] buttons = getAllOneHundredButtonsAsArray();   
    1. for (Button button : buttons) {   
  •     button.setOnClickListener(new View.OnClickListener() {   
  •         public void onClick(View v) {   
  •             showToast(v.getText());   
  •         }   
  •     });   
  • }  
  • Short and elegant, so what’s wrong with it? At each iteration, a new OnClickListener object is instantiated. Since each one is exactly the same, there is no good reason to create 100 of them. Instead, you can create a single, named, inner class, instantiate it once, then pass that to the setOnClickListener() method. For instance:
     
    class MyActivity extends Activity {   
    1.   
    2.     public void myMethod() {   
  •        MyClickHandler handler = new MyClickHandler();   
  •         Button[] buttons = getAllOneHundredButtonsAsArray();   
  •         for (Button button : buttons) {   
  •             button.setOnClickListener(handler);   
  •         }   
  •     }   
  •   
  •     class MyClickHandler implements View.OnClickListener {   
  •         public void onClick(View v) {   
  •             showToast(((Button) v).getText());   
  •         }   
  •     }   
  • }  
  • If you prefer anonymity, you can still assign an anonymous inner class to a variable and use that, like so:
     
    class MyActivity extends Activity {   
    1.   
    2.     public void myMethod() {   
  •         View.OnClickListener handler = new View.OnClickListener() {   
  •                 public void onClick(View v) {   
  •                     showToast(((Button) v).getText());   
  •                 }   
  •             };   
  •   
  •         Button[] buttons = getAllOneHundredButtonsAsArray();   
  •         for (Button button : buttons) {   
  •             button.setOnClickListener(handler);   
  •         }   
  •     }   
  • }  
  • The method is up to you, but keep in mind the potential memory and performance issues that instantiating a bunch of objects may have.

    A Note on Nuances

    This tutorial is meant to be an introductory guide to inner classes in Java. There are style considerations and nuances when using inner classes in different and creative ways. Even beyond that, you can explore further to learn more about the internal effects and marginal performance differences that can show up when you use nested classes in different ways. All of this, however, is well beyond the scope of this tutorial.

    A Quick Note On Terminology

    Although we have tried to be consistent with the terminology on nested and inner classes, the terminology is not always consistent from various online and offline resources. The following are a list of terms from the current Sun/Oracle documentation, which is as good as any for being authoritative on Java:
    • Nested Class: a class defined inside another class
    • Static Nested Class: a static class defined inside another class
    • Inner class: a non-static nested class defined inside another class
    • Local Inner Class: a class defined within a method
    • Anonymous Inner Class: an unnamed class defined within a method
    Confused? You can use the java.lang.Class methods called isLocalClass() and isAnonymous() class on instantiated classes to determine a couple of these properties. An Oracle blog entry attempts to clarify the situation a little, too, with a nice Venn diagram.


    Comments

    Popular posts from this blog

    Android Objective type Question and Answers

    Android Questions and Answers for written exams

    SCJP1.6 Question and Answers