|
Java Programming Language Basics
Java Bits
Making Sense of the Java Class Libraries
Program Challenge
For More Information
Java Programming Language Basics
Understanding Inner Classes
The Java programming language is class based. Everything you do must live within a class. Classes can be defined in many different ways. Typically, classes are defined at what you can think of as the top level of a file:
class X {
}
But, that isn't the only way. Classes can be defined inside of other classes, as in the following:
class X {
class Y {
}
}
This second form of class is called an inner class because the class is defined inside another class. There are four different types of inner classes: top-level, member, local, and anonymous.
Top-level inner classes are just like other outer class definitions. The key differences are that the class is declared within another class, and the inner class gets the static keyword added before the class keyword:
class OuterClass {
static class InnerClass {
}
}
When class X is compiled, two .class files are generated, one for InnerClass and one for OuterClass.
This leads us to the all-important question: How do you reference inner classes? Because inner classes are defined within outer classes, you can't just use the name of the inner class on its own (unless you're within the outer class). Instead, top-level inner classes require you to prefix the inner class name with the outer class name. For instance, in the previous example, to reference InnerClass, you use the name OuterClass.InnerClass. Naming is easily done; just place a period between the outer and inner class names.
Why would anyone use inner classes in this manner? By placing a top-level class (or interface) within another class, you're telling users that the inner class is related to the outer class that contains it.
For instance, the java.util.Map interface provides a data structure for key-value pairs. The interface has an inner interface named Entry, representing each key-value pair entry within the map. By defining Entry within the Map interface, the Entry interface is logically connected to the Map interface. It clarifies the role of Entry for users of Map and doesn't cause confusion with other classes named Entry related to other data structures.
Definitions for top-level classes (and interfaces) can have access modifiers to restrict availability. Just like with methods and variables, you can use any of the access modifiers: private, protected, public, and the default unnamed access. Using the modifiers with inner classes work similarly as with methods and variables.
The second form of inner class is called a member class, you create by not using the keyword static. The key difference between a member class and top-level class is that with a member class you have access to the member fields and methods of the enclosing class. That means that each instance of the inner class is tied to one and only one instance of the outer class.
class OuterClass {
class InnerClass {
}
}
From the inner class, if you need access to the current instance of the outer class, you specify the class name followed by a period and the this keyword, as in OuterClass.this. this provides access to potentially hidden methods and fields where both the inner and outer class use the same named method or variable.
For instance, in the following class definition, both the inner and outer classes share a variable named count. In order to access the outer class's variable, you must prefix the variable name with the outer class name and this.
class OuterClass {
int count = 0;
class InnerClass {
int count = 10000;
public void display() {
System.out.println("Outer: " + OuterClass.this.count);
System.out.println("Inner: " + count);
}
}
?
}
The third type of classes isn't defined at the top-level within a class. Instead, they are defined within methods. These are typically called local classes, like local variables. The benefit of using local classes is being able to define classes where you use them and to better limit their accessibility. If a class is only defined within a method, then it isn't accessible outside the method.
Does this seem counterproductive? If the class isn't accessible outside the method, why do you need to define a class in the first place? Typically, what happens is the local class is defined to implement an interface. What is exposed outside the method is the interface, typically as a return value.
Here's an example method that contains several local inner classes:
public ActionListener getListener(int type) {
class TypeOneListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Got One");
}
}
class TypeTwoListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.out.println("Got Two");
}
}
ActionListener returnValue = null;
switch(type) {
case 1:
returnValue = new TypeOneListener();
break;
case 2:
returnValue = new TypeTwoListener();
break;
}
return returnValue;
}
When using local inner classes, any variables local to the method that are accessed from within the inner class must be declared final.
The final type of inner class to describe is called anonymous classes. In the local classes example, the classes named TypeOneListener and TypeTwoListener were defined. However, since the class names are inaccessible to everyone outside the outer class definition, the need to name them seems unnecessary. Instead of having to come up with names for the local classes, you can just define them in place:
returnValue = new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.out.println("Got Three");
}
};
What you are doing here is saying "new InterfaceName()", which is essentially saying "call the constructor for the interface." It isn't the interface itself here, but the implementation of that interface. Imagine an unnamed class that extends Object and implements the interface name. Since the class has no name, you can't actually call a constructor. What you must do though is implement every method of the interface in the definition. Otherwise, you won't have a valid implementation and the code won't compile.
Notice that trailing semicolon at the end. Since the inner class definition is a part of the assignment statement, the semicolon is required to end the assignment statement.
You'll find these anonymous inner classes a popular feature in IDE tools. Tools such as JBuilder generate the code to connect the event handling code of components. When you create the code by hand, be sure to match up the parentheses and braces.
Test what you learned about inner classes with this online quiz.
Java Bits
Providing Functionality in Separate Classes
The article above showed you how to use inner classes to provide the functionality of a component within the same class, but sometimes you'll want to put that functionality in an entirely separate class. You may want to use separate classes when there is a lot of code, or to keep the UI separate from functionality for troubleshooting and modularity.
Providing functionality of components in separate classes is an easy four step process:
- When registering the component as a listener, instantiate the class that contains the code for functionality with the keyword new.
For instance:
save.addActionListener(new Handler1());
- Make sure the class that provides functionality implements the proper interface.
Such as:
public class Handler1 implements ActionListener
- The class that provides functionality should be declared
public, or a part of the same package.
public class Handler1 implements ActionListener
- Implement the required methods for that interface.
// actionPerformed must be implemented for the ActionListener
// interface.
public void actionPerformed (ActionEvent ae)
For example, in the following application, the GUI code that builds the frame and two buttons is in the TestButton class. The functionality for the buttons is in two separate classes: Handler1 and Handler2.
Download the three classes listed below, and compile the TestButton class. In this process, all three classes compile.
TestButton.java
Handler1.java
Handler2.java
When you click either of the buttons, text prints to the console.
When the application runs, it should look something like the example below:
Making Sense of the Java Class Libraries
JPasswordField Class
The JPasswordField is a lightweight component similar to a text field, but it does not reveal the original characters. Instead, by default it displays the character * for each key typed. The * can be changed to some other character.
Like a text field, JPasswordField has several constructors you may call:
JPasswordField() -- constructs a JPasswordField with null starting text string, and 0 column width.
JPasswordField(Document doc, String txt, int columns) -- constructs a new JPasswordField that uses the given text storage model and the specified number of columns.
JPasswordField(int columns) -- constructs a new empty JPasswordField with the specified number of columns.
JPasswordField(String text) -- constructs a new JPasswordField initialized with text in the field.
JPasswordField(String text, int columns) -- constructs a new JPasswordField initialized with text and columns.
When a user types in the field, the character * appears unless you call the setEchoChar(char c) to change it. For example:
JPasswordField jpf = new JPasswordField(15);
jpf.setEchoChar('#');
The snippet above creates a JPasswordField object 15 columns wide. When a user types into the field, the character # appears for each key pressed instead of the default * character.
To retrieve the text a user did typed, call the getPassword method. In the following example, the getPassword method is called as a new String and assigned to the variable entrdpwd.
String entrdpwd = new String(jpf.getPassword());
The PasswordTest application creates a frame with a PasswordField. Type in a password, and the yellow text field at the bottom of the frame displays if your entry was correct or incorrect. After three tries, the application automatically shuts down.
Download the code
When you compile and run this application, you should get a result that appears something like the figure below:
Program Challenge
Using inner classes, create the following program completely in the main method of the solution class.
- Create a frame with a button that has a label that provides a number.
- Increase by one the value of the number, when the button is selected.
- Override its frameInit method to have its default close operation be EXIT_ON_CLOSE.
See a possible solution to the Challenge:
For More Information
Inner Classes
Anonymous Classes
Class PasswordField
Downloading the Java 2 Platform
For most Java development, you need the class libraries, compiler, tools, and runtime environment provided with the J2SE development kit.
Subscribe to the following newsletters for the latest information about technologies and products in other Java platforms:
- Core Java Technologies Newsletter. Learn about new products,
tools, resources, and events of interest to developers
working with core Java technologies.
- Wireless Developer Newsletter. Learn about the latest
releases, tools, and resources for developers working on
wireless and Java Card technologies and applications.
- Core Java Technologies Tech Tips (formerly JDC Tech Tips)
Get expert tips, sample code solutions, and techniques for
developing in the Java 2 Platform, Standard Edition (J2SE)
You can subscribe to these and other JDC publications on the JDC Newsletters and Publications page
IMPORTANT: Please read our Terms of Use, Privacy, and Licensing policies:
http://www.sun.com/share/text/termsofuse.html
http://www.sun.com/privacy/
http://developer.java.sun.com/berkeley_license.html
Comments? Send your feedback on the Java Technology Fundamentals Newsletter to: dana.nourie@sun.com
Go to the subscriptions page to subscribe or unsubscribe to this newsletter.
ARCHIVES: You'll find the Java Technology Fundamentals Newsletter archives at:
http://developer.java.sun.com/developer/onlineTraining/new2java/supplements/
Copyright 2003 Sun Microsystems, Inc. All rights reserved. 901 San Antonio Road, Palo Alto, California 94303 USA.
Sun, Sun Microsystems, Java, and J2SE are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries.
|