The Essence of OOP using Java, Member Classes
Java Programming Notes # 1636
- Preface
- Preview
- Discussion and Sample Code
- Run the Program
- Summary
- What's Next
- Complete
Program Listing
Preface
This series of lessons is designed to teach you about the essence of Object-Oriented Programming (OOP) using Java.
The first lesson in the series was entitled The Essence of OOP Using Java, Objects, and Encapsulation. The previous lesson was entitled The Essence of OOP using Java, Instance Initializers.
You may find it useful to open another copy of this lesson in a separate browser window. That will make it easier for you to scroll back and forth among the different figures and listings while you are reading about them.
For further reading, see my extensive collection of online Java tutorials at Gamelan.com. A consolidated index is available at www.DickBaldwin.com.
Preview
What can you include in a class definition?
There are several different kinds of items that can be included in a class
definition. As you learned in the early lessons in this series,
the list includes:
- Static variables
- Instance variables
- Static methods
- Instance methods
- Constructors
As you learned in the previous two lessons, the list also includes:
- Static initializer blocks
- Instance initializers
Can also contain other class definitions
In this and the upcoming lessons, you will learn that a class definition
can also contain the following four kinds of inner classes:
- Member classes
- Local classes
- Anonymous classes
- Nested top-level classes and interfaces
This lesson will be dedicated to an explanation of member classes.
Subsequent lessons will explain the other three types of inner classes.
(Note that it is questionable whether a nested top-level class or interface should be referred to as an inner class, because an object of a nested top-level class can exist in the absence of an object of the enclosing class. Regardless of whether the term inner class applies, a nested top-level class is defined within the definition of another class, so its definition is internal to the definition of another class.)
What is a member class?
A member class is a class that is defined inside the definition
of another class, (without the use of the static modifier as is the
case with a nested top-level class).
An object of the member class must be internally linked to an object
of the enclosing class, (which is not the case with a nested top-level
class).
Thus, a member class is truly an inner class. (An object of
the member class cannot exist in the absence of an object of the enclosing
class.)
What about a member interface?
Interfaces defined within classes are implicitly static. This means
that they are always top-level. There is no such thing as
a member interface, a local interface, or an anonymous interface.
Why use member classes?
Probably the most important benefit of member classes has to do with accessing
the other members of enclosing classes. The methods of a member
class have direct access to all the members of the enclosing classes, including
private members. Thus the use of member classes can eliminate the
requirement to connect objects together via constructor parameters.
This is particularly useful in those cases where there is no reason for
an object of a member class to exist in the absence of an object of the
enclosing class, and where the methods of the object of the member class
need access to members of the object of the enclosing class.
Data structures and iterators
For example, there is usually no reason for an Iterator object
to exist in the absence of the data-structure object for which it is designed
to provide iterator services. Also, the iterator object usually needs
to have ready access to the members of the data-structure object, some or
all of which may be private. Thus, a class from which an Iterator
object can be constructed is a good candidate for inclusion as
a member class in the class from which the associated data-structure object
is instantiated.
Listener objects
Another common use for inner classes is in the definition of classes from
which listener objects (which listen for events fired by other objects)
are instantiated. (However, it may be more common to use
anonymous classes than member classes for this purpose.)
What does Flanagan have to say?
Here is how David Flanagan, author of Java in a Nutshell, summarizes
his discussion of member classes.
"A class defined as a member (non-static) of another. Each instance has an enclosing instance, and can use its members. New syntax for this, new, and super. Cannot have static members. Cannot have same name as containing class."
According to Flanagan, the main features of member classes are:
- Every instance of a member class is internally associated with an instance of the class that defines or contains the member class.
- The methods of a member class can implicitly refer to the fields defined within the member class, as well as those defined by any enclosing class, including private fields of the enclosing class.
Smoke and mirrors
Every class definition in a Java program, including nested top-level classes,
member classes, local classes, and anonymous classes, produces a class
file when the program is compiled. According to Flanagan,
"The Java Virtual Machine knows nothing about nested top-level classes and interfaces or the various types of inner classes. Therefore, the Java compiler must convert these new types into standard non-nested class files that the Java interpreter can understand. This is done through source code transformations that insert $ characters into nested class names. These source code transformations may also insert hidden fields, methods, and constructor arguments into the affected classes."
A reference to the containing object
For example, the compiler automatically inserts a private instance variable
in the member class to hold a reference to the containing object.
It also inserts a hidden argument in all constructors for the member class,
and passes the containing object's reference to the constructor for the
member class. The modified constructor saves that reference in the
private instance variable of the object of the member class. Thus
each object instantiated from the member class contains a private reference
to the containing object.
Accessing private members
In those cases where it is necessary for an object of the member class to access private members of the containing object, the compiler automatically creates and uses accessor methods that make such access possible.
Similar to your code
The bottom line is that the code that is automatically produced by the
compiler is probably very similar to code that you would write if you
were writing the program using only of top-level classes. The good
news is that you don't have to write that extra code, and you don't have
to maintain it. The extra code is written for you, and if you modify
your class structure, the extra code is automatically modified accordingly.
Enough talk, let's see some code
The paragraphs that follow will explain a program named InnerClasses06, which is designed specifically to illustrate various characteristics of member classes. I will discuss the program in fragments. A complete listing is shown in Listing 25 near the end of the lesson.
Discussion and Sample Code
- Top-level classes named InnerClasses06, A, and X
- Member classes named B, C, and D.
A$B$C$D.class Figure 1 |
Class containment hierarchy
Once you understand the class file naming convention, you can determine from the file names in Figure 1 that class B is a member class of class A. (The class file named A$B.class indicates that the class named B is a member of the class named A.)
Similarly, class C is a member of class B, and class D is a private member of class C. (However, you cannot tell from the class file names that class D is private.)
Program behavior
An object is instantiated from the class named A. This makes it possible to instantiate an object of the member class named B. The object of the class named B is internally linked to the object of the class named A.
(This causes the instance variable, constructor parameter, and accessor methods discussed above to be automatically created to link the object of the class named B to the object of the class named A.)The object of the class named B is used to instantiate an object of the member class named C. This object is of the class C is linked to the object of the class named B.
Instantiate additional objects of classes A and B, plus an object of class D
When the object of the class named C is instantiated, the constructor for that class instantiates separate objects of the classes named A and B, and also instantiates an object of the private member class named D.
(We will see later that the new and separate object of the class named B continues to be internally linked to the original object of the Class named A, and is not internally linked to the new object of the class named A.)Instantiation of the object of class D illustrates the use of private member classes.
(A top-level class cannot be private.)Perform a variety of operations
A variety of operations are performed from within the methods belonging to the object of the class C to illustrate the attributes and behavior of objects instantiated from member classes. Comments in the code explain the purpose of each of those operations.
Many of those operations produce screen output, which will be shown in conjunction with the code that produced the output.
The main method
The main method of the controlling class named InnerClasses06, is shown in Listing 1.
public class InnerClasses06{
|
The code in Listing 1 instantiates an object of the member class named C and invokes the method named cShow on that object.
(Note that it is necessary to first instantiate objects of the enclosing classes named A and B before the object of the member class named C can be instantiated.)An independent top-level class named X
Listing 2 shows the definition of an independent top-level class named X.
class X{//extends Object by default
|
This class will be extended by the class named C, which is a member of the class named B, which is a member of the class named A. This will illustrate that the inheritance hierarchy is independent of the containment hierarchy.
As you can see in Listing 2, the class named X overrides the toString method to identify itself when invoked.
(The toString method is automatically invoked whenever an object's reference is passed as a parameter to the println method.)The top-level class named A
Listing 3 shows the beginning of the top-level class named A.
class A{
|
Listing 3 shows the declaration of two instance variables and three class variables in the class named A. All of the variables are private, and some are initialized when declared.
The three class variables will be used to maintain a count of the number of objects instantiated from the classes named A, B, and C.
(Because member classes cannot contain static members, the counter variables for the member classes named B and C were placed in the top-level class named A instead of placing them in their respective class definitions.)Constructor for class A
Listing 4 shows the constructor for the top-level class named A.
A(int val){//top-level class constructor
|
Whenever an object of the class named A is instantiated, the constructor does the following:
- Saves the value of an incoming parameter in a private instance variable named aVar.
- Increments the object counter named objCntA, maintaining a count of the objects instantiated from class A.
- Saves the value of the object counter in an instance variable named objNumber to identify the specific object.
- Displays a message showing the identification of the object being instantiated.
The code in Listing 1 instantiates a new object of the class named A, passing the integer value 1 as a parameter to the constructor. As a result, the code in the constructor shown in Listing 4 produces the screen output shown in Figure 2.
In xstr for A, objCntA = 1 Figure 2 |
As you can see from the value of the object counter in Figure 2, this is the first object instantiated from the class named A.
(The value passed, as a parameter to the constructor, is not displayed by the code in the constructor. That value will be displayed later.)The method named aShow
The class named A also defines a private method named aShow. I will defer my discussion of that method until later when it is invoked.
The member class named B
Listing 5 shows the beginning of the member class named B.
class B{//member class of A
|
If you examine the complete listing of the program in Listing 25 near the end of the lesson, you will note that the class named B is defined internal to the class named A. In other words, the beginning of the definition of the class named B appears before the curly brace that signals the end of the definition of the class named A. Thus, the class named B is a member class of the class named A.
The code in Listing 5 declares two private instance variables and initializes one of them.
Constructor for class B
Listing 6 shows the entire constructor for the class named B.
B(int val){//constructor
|
Whenever an object of the class named B is instantiated, the constructor does the following:
- Saves the value of an incoming parameter in a private instance variable named bVar.
- Increments the object counter named objCntB, which is a class variable of the containing top-level class named A, maintaining a count of objects instantiated from class B.
- Saves the value of the object counter in an instance variable named objNumber to identify the specific object.
- Displays a message showing the identification of the object being instantiated.
Listing 1 shows the instantiation of a new object of class B, immediately following the instantiation of an object of class A. The object instantiated from the member class named B is linked to the object instantiated from the top-level class named A.
The constructors for the classes named A and B produce the two lines of output shown in Figure 3, the first of which is a repeat of the output shown in Figure 2.
In xstr for A, objCntA = 1 Figure 3 |
The method named bShow
The class named B also defines a private method named bShow. As with the method named aShow mentioned earlier, I will defer a discussion of bShow until later when it is invoked.
The member class named C
Listing 7 shows the beginning of a member class named C.
class C extends X{//member class of B
|
Class C is a member of the class named B. In other words, the beginning of the definition of the class named C begins before the curly brace that ends the definition of the class named B.
The code in Listing 7 declares several instance variables for the class named C, and initializes two of them. The purpose of these variables will become clear later when they are used.
(Note also that class C extends class X, in order to illustrate that the class containment hierarchy is independent of the inheritance hierarchy.)Constructor for class C
Listing 8 shows the beginning of the constructor for the class named C.
C(int val){//constructor
|
Whenever an object of the class named C is instantiated, the constructor code shown in Listing 8 does the following:
- Saves the value of an incoming parameter in a private instance variable named cVar.
- Increments the object counter named objCntC, which is a class variable of the class named A, maintaining a count of objects instantiated from class C.
- Saves the value of the object counter in an instance variable named objNumber to identify the specific object.
- Displays a message showing the identification of the object being instantiated.
Listing 1 shows the instantiation of a new object of class C, immediately following the instantiation of an object of class B. The object instantiated from the member class named C is linked to the object instantiated from the member class named B. Similarly, the object instantiated from the member class named B is linked to the object instantiated from the top-level class named A.
The constructors for the classes named A, B, and C produce the three lines of output shown in Figure 4, the first two of which are repeated from Figures 2 and 3.
In xstr for A, objCntA = 1 Figure 4 |
The output shown in Figure 4 demonstrates that the code in Listing 1 causes the constructors for the three classes to be executed in succession.
At this point, I am going to put the discussion of the class named C on hold for a moment and discuss another member class named D.
The private member class named D
Top-level classes cannot be private. However, member classes can be private provided that the using code is consistent with the use of private members. To demonstrate this, the class named C contains a private member class named D, which is shown in its entirety in Listing 9.
private class D{//member class of C
|
The most significant thing about the class named D is that it is declared private.
When an object is instantiated from the class named D, it displays a couple of messages, one of which provides the name of the class file produced by the compiler to represent the class named D. We will see those messages shortly in conjunction with the instantiation of an object of the class named D.
Returning to the constructor for class C ...
Listing 10 shows the next statement in the constructor for the class named C, which instantiates an object of its private member class named D.
new D(); |
The code in Listing 10 causes the constructor for the class named D to be executed, producing the screen output shown in Figure 5.
Construct obj of private class D. Figure 5 |
As mentioned earlier, comparing the class file name in Figure 5 with the class file naming convention for member classes, you can determine that D is a member of C, C is a member of B, and B is a member of A.
Instantiate independent objects of classes A and B
The remaining constructor code for class C is shown in Listing 11.
refToA = new A(10); |
The code in Listing 11 instantiates new and independent objects of the classes named A and B, both of which are enclosing classes of the member class named C.
(Note that the parameter values passed to the constructors are different than was the case for the objects instantiated in Listing 1. We will see the result of that later.)I will display information about these two objects later, which will show that the new object of the member class named B is linked to the original object of the enclosing class named A.
The screen output
In the meantime, when these two objects are instantiated, their constructors are executed, producing the screen output shown in Figure 6.
In xstr for A, objCntA = 2 Figure 6 |
In each case, the value of the object counter shows that this is the second object instantiated from each of these two classes.
Methods aShow, bShow, and cShow
The classes named A, B, and C, each contain display methods named aShow, bShow, and cShow respectively.
The method named cShow is rather long, and I will discuss it in detail shortly. For now, suffice it to say that code in cShow invokes the private method named bShow in the containing object to which it is linked. Therefore, this will be an appropriate time to examine the method named bShow, which is defined in the member class named B.
The method named bShow
The bShow method, defined in the member class named B, is shown in Listing 12. It is important to note that this is a private method.
private void bShow(){
|
When this method is invoked, it does the following:
- Displays the value of the constructor parameter passed to the object when it was constructed.
- Displays the identification of the object based on the value of the object counter when it was constructed.
- Invokes the corresponding aShow method of the object of the containing class to which it is linked.
The method named aShow
The aShow method, defined in the top-level class named A, is shown in Listing 13. It is also important to note that this is a private method.
private void aShow(){
|
When this method is invoked, it does the following:
- Displays the value of the constructor parameter passed to the object when it was constructed.
- Displays the identification of the object based on the value of the object counter when it was constructed.
Because cShow invokes bShow, which in turn invokes aShow, we should expect that invocation of the cShow method on an object of the member class named C would display information about the containment hierarchy.
(Simply as another reminder, the containment hierarchy is completely independent of the inheritance hierarchy.)Invoking cShow
Referring once more to Listing 1, we see that the method named cShow is invoked on the object of the class named C when that object is instantiated. We will see the result of that invocation shortly.
The cShow method
Listing 14 shows the beginning of the cShow method.
public void cShow(){
|
The code in Listing 14
- Displays a string separator to help locate the specific output in the large quantity of output produced by the program.
- Displays the object identifier based on the object counter.
- Displays the value passed to the constructor when the object was instantiated.
The code in Listing 14 produced the output shown in Figure 7.
-1- Figure 7 |
As you can see by comparing this with Listing 1, this is the first object instantiated from the class named C, and is the object instantiated from the statement in the main method in Listing 1. (The constructor parameter value is 3.)
Invoke the bShow method
Continuing with the code in the cShow method, the code in Listing 15 invokes the private method named bShow on the containing object of the class B to which this object is linked.
System.out.println("-2-");//separator
|
As you will recall from the previous discussion, the code in the bShow method will, in turn, invoke the aShow method on the containing object of the class named A to which the object of the class B is linked.
The screen output
The code in Listing 15 produces the output shown in Figure 8.
-2- Figure 8 |
As you can see in Figure 8, the linked objects of the classes B and A are the first objects instantiated from those classes. In addition, the saved values of the constructor parameters show that these are the objects that were instantiated by the statement in the main method of Listing 1.
Invoke the aShow method
As I explained earlier, the object of the class C is linked to the containing object of the class named B. The code in Listing 16 shows that the object of the class C is also linked to the containing object of the class A (even though the containing class named A is one level removed in the containment hierarchy).
System.out.println("-3-");//separator
|
The methods of a member class have implicit access to all members (including private members) of all containing classes. Thus, the code in the cShow method, belonging to the object of the class named C, can directly invoke the private aShow method of the containing class named A.
The screen output
Thus, the code in Listing 16 produces the output shown in Figure 9.
-3- Figure 9 |
You can tell by the values displayed in Figure 9 that the aShow method invoked in Listing 16 was invoked on the same object on which the aShow method was invoked by the code in Listing 15. However, in Listing 15, the bShow method was invoked first, which in turn invoked the aShow method.
Accessing the object of the class C, and the this keyword
The syntax used with the keyword this is somewhat different for member classes and contained objects than is the case for top-level classes. For example, continuing with the method named cShow, the code in Listing 17 shows five different ways to access the object instantiated from the member class named C in order to get and display the name of the class file that represents the member class named C.
System.out.println("-4-");//separator
|
The screen output
All five statements in Listing 17 display the name of the same class file, as shown in Figure 10.
-4- Figure 10 |
Obviously in this situation, the last three statements in Listing 17 are overly complex. There is no particular problem writing code in the method named cShow to gain access to the object to which the method belongs. It isn't even necessary to use this to refer to that object, although the use of the hidden reference this may make the code more readable.
Accessing the containing object of the class B
However, things get a little more complicated when you need to gain access to a containing object, such as the containing object instantiated from the class named B.
The two statements shown in Listing 18 gain access to the containing object of the class named B. Each statement gets and displays the name of the class file that represents the member class named B. (Note the use of the keyword this in these statements.)
System.out.println("-5-");//separator
|
The screen output
The output produced by the code in Listing 18 is shown in Figure 11. Once again, both statements get and display the name of the same class file.
-5- Figure 11 |
Accessing the containing object of the class named A
Finally, the code in Listing 19 gains access to the containing object of the class named A. (Once again, note the use of the this keyword in the statement in Listing 19.)
System.out.println("-6-");//separator
|
The code in Listing 19 produces the output shown in Figure 12.
(Since the class named A is a top-level class, the name of the class file is the same as the name of the class, with no $ characters inserted by the compiler.)
-6- Figure 12 |
Investigate independent objects of classes A and B
Recall that when the object of the member class named C was instantiated, the constructor for the class instantiated independent objects of the enclosing classes named A and B, and saved those object's references in instance variables of the class named C.
(See Listing 11, noting the parameter values of 10 and 20 passed to the constructors for A and B. Recall that the constructors for A and B save those parameter values in private instance variables named aVar and bVar.)Display variable values and class file names
The code in Listing 20 displays the values stored in the private instance variables belonging to those objects. The code in Listing 20 also displays the names of the class files representing the classes from which those objects were instantiated.
System.out.println("-7-");
|
Screen output
The code in listing 20 produces the output shown in Figure 13.
-7- Figure 13 |
There should be no surprises in the output shown in Figure 13. The values of the instance variables match the parameter values passed to the constructors in Listing 11 when the objects were instantiated. The class file names match what you already know to be true from previous discussions earlier in this lesson.
Invoke the private bShow method
The code in Listing 21 is somewhat more interesting. This code invokes the private bShow method on the separate object instantiated from the class named B in order to identify the object to which that object is linked.
System.out.println("-8-");
|
The screen output
The output produced by the code in Listing 21 is shown in Figure 15. Even though this object of the member class B was instantiated from within the constructor for the member class named C, the object of the class named B is internally linked to the object of the class named A that was originally used to instantiate the object of the class named C.
(See Listing 1 where the objects of classes named A, B, and C were originally instantiated. This object of the class named B is a different object from the object of the class named B instantiated in Listing 1. This object of the class named B was instantiated by the code in Listing 11.)
-8- Figure 15 |
How is this determined from Figure 15?
The second line in Figure 15 shows that a parameter value of 20 was received by the constructor when the object of the class named B was instantiated. This corresponds to the instantiation of the object by the code in the constructor in Listing 11.
The third line in Figure 15 shows that this was the second object instantiated from the class named B. (See the definition of the bShow method in Listing 12, which displays the value stored in a variable that is used to save the object number.)
The proof of the pudding
Now recall that the method named bShow (belonging to an object of the class B) invokes the method named aShow belonging to the object of the class named A to which it is internally linked.
The fourth line in Figure 15 shows the value of the parameter passed to the constructor for the object of class A when that object was instantiated. (See the definition of the aShow method in Listing 13.) This value corresponds to the value that was passed to the constructor for the original object of class A when it was constructed in Listing 1.
(It does not correspond to the value passed to the constructor for the class named A when the object of the class A was constructed in Listing 11.)The fifth line in Figure 15 shows that the object was the first object instantiated from the class named A.
Both B objects links to the same A object
Thus, both objects instantiated from the class named B in this program are internally linked to the same object instantiated from the class named A, which is the enclosing class for the class named B.
(However, had I instantiated the new object of the class B using a statement such as the following,Invoke the aShow method on the other A object
new A(100).new B(200).bShow();
the new object of the class B would have been linked to the new object of the class A rather than being linked to the original object of the class A. As you can see, keeping mental track of which object is linked to which other object could become complicated.)
The code in Listing 22 invokes the private aShow method on the independent object of the class A that was instantiated in the constructor for class C, shown in Listing 11.
System.out.println("-9-");
|
The output produced by the code in Listing 22 is shown in Figure 16.
-9- Figure 16 |
It should come as no surprise that this object of the class A was instantiated with a constructor parameter value of 10, and that it was the second object of the class named A that was constructed. This is simply a matter of code in the constructor for class C instantiating an object of a top-level class, and is no different in concept from instantiating an object of the member class B, also shown in Listing 11.
Inheritance and containment hierarchies are independent
The remaining code is designed to demonstrate that the containment hierarchy is completely independent of the inheritance hierarchy.
The class named C is a member of (is contained in) the class named B. That constitutes a part of the containment hierarchy. The class named C also extends the class named X, which in turn extends the class named Object. That constitutes the inheritance hierarchy.
Overridden toString methods
I'm going to set the discussion of the method named cShow on hold momentarily, and return to that discussion shortly. The class named X inherits, and overrides the toString method, as shown in Listing 2. When this version of the toString method is invoked, it returns the string "toString in Class X".
The class named C, which extends the class named X, also overrides the toString method as shown in Listing 23.
public String toString(){
|
When this version of the toString method is invoked, it returns the string "toString in Class C". We will see the impact of overriding these two methods later.
Illustrate the inheritance hierarchy
Returning to the cShow method, the code in Listing 24 illustrates the inheritance hierarchy to which the class named C belongs by getting and displaying the value stored in the instance variables named className belonging to the object instantiated from the class named C.
(The object contains two instance variables having the name className. One of these instance variables was contributed to the object by the superclass named X. The other was contributed to the object by the class named C.)
System.out.println("-10-");
|
Two instance variables named className
The String value X was stored in one of the instance variables named className by the initialization of the variable shown in Listing 2. The String value C was stored in the other instance variable named className by the initialization of the variable shown in Listing 7.
(Note that the variable named className is protected in the class named X. A subclass method cannot access a private variable in a superclass. To be accessible by a subclass method, the superclass variable must be protected, package private, or public.)Two overridden toString methods
An object instantiated from the class named C also contains two overridden versions of the toString method. One version of the method was contributed to the object by the superclass named X. The other version was contributed to the object by the class named C.
Invoke the toString methods
The code in Listing 24 also invokes the two overridden toString methods belonging to the object instantiated from the classes named C.
As explained earlier, one version of the toString method is overridden in the class named X and the other version is overridden in the class named C.
(Note the use of the super keyword to access the variable named className and the method named toString contributed to the object by the superclass named X.)The code in Listing 24 also signals the end of the cShow method.
The screen output
The output produced by the code in Listing 24 is shown in Figure 17.
-10- Figure 17 |
There should be no surprises in Figure 17. Figure 17 shows that even though the class named C is contained in the class named B, the superclass of C is X, and is not B. To repeat, the containment hierarchy is entirely independent of the inheritance hierarchy.
(Note, however, that there is nothing to prevent you from establishing an inheritance relationship between a member class and one of its containing classes if such a relationship will serve your needs. For example, in this program, it would be technically acceptable for the class named B to extend the class named A provided that either:And that is probably more than you ever wanted to know about the detailed relationships involving member classes. However, once you start using member classes, you will need to keep these relationships in mind.
- A noarg constructor is provided for the class named A, or
- The constructor for the class named B invokes the parameterized constructor belonging to the class named A.)
Run the Program
At this point, you may find it useful to compile and run the program
shown in Listing 25 near the end of the lesson.
Summary
- Member classes
- Local classes
- Anonymous classes
- Nested top-level classes and interfaces
A member class is a class that is defined inside the definition of another class without being declared static.
An object of the member class must be internally linked to an object of the enclosing class.
A member class is truly an inner class because an object of the member class cannot exist in the absence of an object of the enclosing class.
The methods of a member class have direct access to all the members of the enclosing classes, including private members. Thus the use of member classes can eliminate the requirement to connect objects together via constructor parameters. This is particularly useful in those cases where there is no reason for an object of a member class to exist in the absence of an object of the enclosing class, and where the methods of the object of the member class need access to members of the object of the enclosing class.
The containment hierarchy of member classes is independent of the inheritance hierarchy. However, it is technically possible to establish an inheritance relationship between a member class and one of its enclosing classes.
Member classes may be declared private, and may be instantiated from code that would normally have access to a private member at that level.
What's Next?
The next lesson in this series will
explain and discuss local classes. Subsequent lessons will explain
anonymous classes and top-level nested classes.
Complete Program Listing
A complete listing of the program discussed in this lesson is show in Listing 25 below.
/*File InnerClasses06.java |
Copyright 2003, Richard G. Baldwin. Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.
About the author
Richard Baldwin is a college professor (at Austin Community College in Austin, Texas) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.
Richard has participated in numerous consulting projects, and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas. He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.
Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.
-end-
