This quick lesson discusses a number of tips for working with inner classes in Java. This lesson is part of an ongoing series of tutorials for developers learning Java in order to develop Android applications.
For a thorough discussion of nested and inner classes, including anonymous inner classes, see our tutorial called Learn Java for Android Development: Inner Classes
What are (Anonymous) Inner Classes?
In Java, classes can be nested within one another in order to organize data and functionality in an orderly fashion. Anonymous inner classes are basically developer shorthand, allowing the developer to create, define, and use a custom object all in one go. Anonymous inner classes are frequently used in Android to define handlers for controls, define and launch new threads, and create and return custom objects in methods without cluttering code with unnecessary setup.For a thorough discussion of nested and inner classes, including anonymous inner classes, see our tutorial called Learn Java for Android Development: Inner Classes
Accessing Outside Variables with the Final Keyword
Sometimes you want to access information available outside of the inner class. Consider the following example. You’ve got a screen with two controls: a Button and a TextView. Each time the user clicks on the Button control, the TextView control is updated with the current time. Within the Activity class associated with this layout, you could implement this functionality as follows:Button myButton = (Button) findViewById(R.id.ButtonToClick);The above code will run and behave as expected. However, let’s say you really wanted to declare the TextView control outside the inner class and use it for other purposes as well. Perhaps you’d try to do this instead:
myButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SimpleDateFormat formatter = new SimpleDateFormat("h:mm:ss a");
String strWhen = formatter.format(new Date());
TextView myTextview = (TextView)
findViewById(R.id.TextViewToShow);
myTextview.setText("Clicked at " + strWhen);
}
});
TextView myTextview = (TextView) findViewById(R.id.TextViewToShow);Unfortunately, this won’t compile. Instead, you’ll get the compile error “Cannot refer to a non-final variable myTextview inside an inner class defined in a different method”. As the error suggests, what you need to do is make the TextView variable final, so that it is accessible within the inner class’s onClick() method:
Button myButton = (Button) findViewById(R.id.ButtonToClick);
myButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SimpleDateFormat formatter = new SimpleDateFormat("h:mm:ss a");
String strWhen = formatter.format(new Date());
myTextview.setText("Clicked at " + strWhen);
}
});
final TextView myTextview = (TextView) findViewById(R.id.TextViewToShow);This code will indeed compile and run as expected.
Button myButton = (Button) findViewById(R.id.ButtonToClick);
myButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SimpleDateFormat formatter = new SimpleDateFormat("h:mm:ss a");
String strWhen = formatter.format(new Date());
myTextview.setText("Clicked at " + strWhen);
}
});
Working with Final Variables in Java
By making the TextView variable final in the previous example, you’ve made it available to the anonymous inner class (or any inner class defined within its scope, for that matter). Here are some other things to know about final variables:- You cannot change the value (r-value) of a final variable. For example, if you tried to assign the myTextview variable to another control within the onClick() method of the inner class, you would get the compile error “The final local variable myTextview cannot be assigned, since it is defined in an enclosing type.”
- You can call a final variable’s methods, provided you have access. This is what allows us to make the call to the TextView’s setText() method. This does not change the value of the variable myTextview, it simply changes what is displayed to the screen—a subtle but important difference. (The reason for this is simply that the reference to the object can't change, but the object itself can change through its methods.)
- A final variable’s value need not be known at compile time, whereas a static variable is known at compile time.
- The final keyword is often paired with the static variable (a field or variable tied to all instances of a class, instead of a single instance) to create a constant for use within your application, as in: public static final String DEBUG_TAG, used for LogCat logging purposes throughout your Activity.
Inner Classes and the "This" variable
In Java, you can use the special this reference to refer to the specific instance of an object. So what happens when you have a class with an inner class, or, for that matter, an anonymous inner class? Well, the inner class can access the special this instance of the enclosing class, and it has a this reference of its own as well. To access the this instance for the inner class, simply use the this syntax. To access the this instance of the enclosing class, you need to tack on the name of the enclosing class, then a dot, then this. For example:MyEnclosingClass.thisTake a look at the full implementation of the anonymous inner class, as discussed above, in the full context of its enclosing class, here called ClassChaosActivity:
package com.androidbook.classchaos;The log output for the button click proceeds as follows:
import java.text.SimpleDateFormat;
import java.util.Date;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class ClassChaosActivity extends Activity {
public static final String DEBUG_TAG = "MyLoggingTag";
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final TextView myTextview = (TextView) findViewById(R.id.TextViewToShow);
Button myButton = (Button) findViewById(R.id.ButtonToClick);
myButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
SimpleDateFormat formatter = new SimpleDateFormat("h:mm:ss a");
String strWhen = formatter.format(new Date());
myTextview.setText("Clicked at " + strWhen);
Log.v(DEBUG_TAG, "this Class name: " + this.getClass().getName());
Log.v(DEBUG_TAG, "this extends interface named: " + this.getClass().getInterfaces()[0].getName());
Log.v(DEBUG_TAG, "this Enclosing class name: " +this.getClass().getEnclosingClass().getName());
Log.v(DEBUG_TAG, "this Is anonymous class? " + this.getClass().isAnonymousClass());
Log.v(DEBUG_TAG, "ClassChaosActivity.this Class name: " + ClassChaosActivity.this.getClass().getName());
Log.v(DEBUG_TAG, "ClassChaosActivity.this Super Class name: " + ClassChaosActivity.this.getClass().getSuperclass().getName());
Log.v(DEBUG_TAG, "ClassChaosActivity.this Is anonymous class? " + ClassChaosActivity.this.getClass().isAnonymousClass());
}
});
}
}
10-24 18:18:53.075: VERBOSE/MyLoggingTag(751): this Class name: com.androidbook.classchaos.ClassChaosActivity$1As you can see, the this keyword on its own refers to the “closest” class—the inner class. Although the inner class is anonymous, Java does give it a number ClassChaosActivity$1 to keep track of it. While not useful to developers per se, this shows that the anonymous inner class is treated like any other class internally. Meanwhile, we access the enclosing class instance using the ClassChaosActivity.this syntax.
10-24 18:18:53.085: VERBOSE/MyLoggingTag(751): this extends interface named: android.view.View$OnClickListener
10-24 18:18:53.085: VERBOSE/MyLoggingTag(751): this Enclosing class name: com.androidbook.classchaos.ClassChaosActivity
10-24 18:18:53.095: VERBOSE/MyLoggingTag(751): this Is anonymous class? true
10-24 18:18:53.095: VERBOSE/MyLoggingTag(751): ClassChaosActivity.this Class name: com.androidbook.classchaos.ClassChaosActivity
10-24 18:18:53.105: VERBOSE/MyLoggingTag(751): ClassChaosActivity.this Super Class name: android.app.Activity
10-24 18:18:53.105: VERBOSE/MyLoggingTag(751): ClassChaosActivity.this Is anonymous class? false
No comments:
Post a Comment