Maarten Pennings'
Android development: Inline class extension
fampennings / maarten / android / 01inlineextend

Inline class extend

Download the source or a presentation of this article.

Introduction

Once the Android app "hello world" has been written, the next app very likely has a button. The web shows how to handle the button click:

button.setOnClickListener( new OnClickListener() {
  @Override
  public void onClick(View v) {
    // do something when the button is clicked
  }
});

To be honest, this (especially the red part) bewildered me the first time. What does new OnClickListener() {...} mean? This page tries to explain the construction.

Class and instantiation

Java is an object oriented language; instantiating an object of a class is happening all the time. For example, suppose we have the following class definition

class C {
  protected String name;

  public C( String n ) {
    name = n;
  }

  public String Greet( ) {
    return "C object "+name+" says hi";
  }
}

We could then have the following instantiation

C o1= new C("o1");
String s1= o1.Greet();

If you want to test this yourself in an Android environment, make sure that the class C definition is in its own C.java file, and that it is in the same package as your activity (e.g. the line package nl.fampennings.inlineextend; is the first in both files). As in most object oriented languages (but not all, e.g. Delphi), the constructor must have the same name as the class (by using overloading, it is possible to have multiple constructors).

We could add the above object allocation in the click handler of a "test" button, as is illustrated in the fragment below.

public void onTestClick(View v) {
  // Test 1
  C o1= new C("o1");
  String s1= o1.Greet();
  Toast.makeText( getApplicationContext(), s1, Toast.LENGTH_SHORT ).show();
}

The resulting greeting is shown using a so-called toast (a pop-up), see the figure below for the result.


The toast showing the greeting of object o1.

Extends

Sometimes, a subclass needs to be derived from the base class C, or in other words, the base class C needs to be extended. This can be done in the following fashion (again, class CC should be stored in its own file CC.java within the same package).

class CC extends C {
  public CC( String n ) {
    super(n);
  }

  @Override
  public String Greet( ) {
    return "CC object "+name+" says hi";
  }
}

The red fragments deserve some attention. First of all, note the syntax for extending class C: extends C. Secondly, note how the new constructor CC calls the one of its super class: super(n). Finally note that in Java, every method in a class is always virtual. Using the @Override annotation help catching errors. The developer website explains @Override as follows: "Annotation type used to mark methods that override a method declaration in a superclass. Compilers produce an error if a method annotated with @Override does not actually override a method in a superclass.".

To instantiate an object of the extended class is the same as before:

public void onTestClick(View v) {
  // Test 1
  ...
  // Test 2
  CC o2= new CC("o2");
  String s2= o2.Greet();
  Toast.makeText( getApplicationContext(), s2, Toast.LENGTH_SHORT ).show();
}


The toast showing the greeting of extended object o2.

Inline extension

We now come to the construction used by click-handlers as mentioned in the introduction. The click-handler construction uses an implicit subclass. An implicit subclass does not have an explicit class definition as shown in the previous paragraph. Rather, an implicit subclass is defined using an inline class extension (also known as inlined class or anonymous class). The following fragment, shows the creation of an object o3 from a base class C which has an inline extension (marked red).

public void onTestClick(View v) {
  // Test 1
  ...
  // Test 2
  ...
  // Test 3
  C o3= new C("o3") {
    @Override
    public String Greet( ) {
      return  "C{} object "+name+" says hi";
    }
  };
  String s3= o3.Greet();
  Toast.makeText( getApplicationContext(), s3, Toast.LENGTH_SHORT ).show();
}

Inline extension is not restricted to overriding existing methods. It is also possible to add new methods. In the fragment below, we have added a helper method with the inventive name Helper().

C o3= new C("o3") {
  public String Helper( ) {
    count++;
    return " ("+count+")";
  }
  @Override
  public String Greet( ) {
    return  "C{} object "+name+" says hi"+Helper();
  }
};
String s3= o3.Greet();
Toast.makeText( getApplicationContext(), s3, Toast.LENGTH_SHORT ).show();

The second time we press the test button, we get the following toast.


The toast showing the greeting of object o3.

Scope

What surprised me most, was that the inlined class, being local to the definition of the activity class (being an nested or inner class), has access to all members of the actvity class. To be more precise, the member variable count from the previous fragment, is actually a member of the outer activity class.

public class InlineExtendActivity extends Activity {
  private int count= 0;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
  }

  public void onTestClick(View v) {
    // Test 1
    C o1= new C("o1");
    String s1= o1.Greet();
    Toast.makeText( getApplicationContext(), s1, Toast.LENGTH_SHORT ).show();

    // Test 2
    CC o2= new CC("o2");
    String s2= o2.Greet();
    Toast.makeText( getApplicationContext(), s2, Toast.LENGTH_SHORT ).show();

    // Test 3
    C o3= new C("o3") {
      public String Helper( ) {
        count++;
        return " ("+count+")";
      }
      @Override
      public String Greet( ) {
        return "C{} object "+name+" says hi"+Helper();
      }
    };
    String s3= o3.Greet();
    Toast.makeText( getApplicationContext(), s3, Toast.LENGTH_SHORT ).show();
  }

}

Remaining questions

I'm wondering how inner class access members of the outer class; how is implemented by the Java compiler? It seems that the compiler arranges this by adding an extra private instance variable this$0 which links the inner class to the enclosing class. This variable is initialized from an extra argument passed to the inner class constructor. That argument, in turn, is determined by the expression which creates the inner class instance; by default it is the object doing the creation (see mit).

Is there also a @New in addition to @Override in case the base class later gets a new method that happens to get the name we had chosen for a fresh method in our derived class. It seems not...

The code fragment in the introduction, new OnClickListener(), creates a new interface. How is that possible? Only an instance of a class can be created, right?


Download the source or a presentation of this article.

home
inline extend
Inline extend




Downloads:
Source
Presentation





Editor:
Maarten Pennings
Laatste wijziging:
8 aug 2011
File:
error