Classes within classes

estrapadesherbetΛογισμικό & κατασκευή λογ/κού

18 Νοε 2013 (πριν από 4 χρόνια και 1 μήνα)

115 εμφανίσεις

Classes within classes

Learn the basics of nested top
-
level classes and inner
classes

Summary

As with fields and methods, Java allows classes to be members of
other classes. This month, Jeff Friesen explores Java's support for
class nesting. He presents
the reasons for that support along with four
categories of nested classes
--

nested top
-
level classes, instance inner
classes, local inner classes, and anonymous inner classes.

For a glossary of terms, a list of tips and cautions, homework, and
additional

material related to this article, visit the associated
study
guide
. (
3,600 words;
February 1, 2002
)

By Jeff Friesen



While teaching Java, I often find that students try to declare methods within other
methods. However, unlike the Pascal language
--

which allows procedures (roughly
equivalent to methods) to
nest

ins
ide other procedures
--

Java does not allow
methods to nest inside other methods. As a result, the Java compiler generates an
error when it encounters the following code fragment, which nests method
innerMethod()

inside method
outerMethod()
:

void outerMet
hod ()

{



void innerMethod ()



{



}

}

In contrast to method nesting, Java, beginning with Java Language Specification 1.1,
supports class nesting; the Java compiler allows one class to appear inside another
class. The following code fragment demonstr
ates the nesting of class
innerClass

inside class
outerClass
:

class outerClass

{



class innerClass



{



}

}

Why does Java support class nesting, and what kinds of nested classes does Java
support? This
Java 101

installment answers these questions. On
ce you finish
reading this article, you will have gained a good working knowledge of class nesting
and can use that knowledge to write powerful Java programs. To begin, let's find out
why Java supports class nesting.

Note


With the release of JDK 1.1 (wh
ich includes Java 1.1),
Sun released the
Inner Classes Specification

document.
That document thoroughly covers nested top
-
level classes
and inner classes. I strongly encourage yo
u to review that
document once you finish reading this article.

Why does Java support class nesting?

Java doesn't have to support class nesting. In fact, if you study the
Inner

Classes
Specification

document, you will discover workarounds to class nesting. However,
there are at least two benefits to Java's support for class nesting:



An improvement in source code clarity



A reduction in name conflicts

Source code clarity improv
es with class nesting because you declare a class closer to
those objects it must manipulate and allow that class's methods to directly access
object fields and call object methods
--

even private fields and methods
--

of
enclosing classes. To understand t
hat benefit, consider a scenario in which a
program must iterate over an array of
Job

objects that exist within an
Employee

object:

Listing 1. JobIterator1.java


// JobIterator1.java


class Job

{



private String jobTitle;




Job (String jobTitle)



{


this.jobTitle = jobTitle;



}




public String toString ()



{


return jobTitle;



}

}


class Employee

{



private String name;



private Job [] jobs;



private int jobIndex = 0;




Employee (String name, Job [] jobs)



{


this.nam
e = name;


this.jobs = jobs;



}




String getName ()



{


return name;



}




boolean hasMoreJobs ()



{


return jobIndex < jobs.length;



}




Job nextJob ()



{


return !hasMoreJobs () ? null : jobs [jobIndex++];



}

}


cla
ss JobIterator1

{



public static void main (String [] args)



{


Job [] jobs = { new Job ("Janitor"), new Job ("Delivery
Person") };



Employee e = new Employee ("John Doe", jobs);



System.out.println (e.getName () + " works the followin
g
jobs:
\
n");



while (e.hasMoreJobs ())



System.out.println (e.nextJob ());



}

}

When run,
JobIterator1

produces the following output:

John Doe works the following jobs:


Janitor

Delivery Person

JobIterator1

consists of classes
Job
,
Employe
e
, and
JobIterator1
.
Job

encapsulates a job title, and
Employee

encapsulates an employee name and an
array of those jobs the employee performs.
JobIterator1

contains a
main()

method that creates
Job

and
Employee

objects, and prints the employee's name
and
jobs.

A close look at
Employee

reveals methods
hasMoreJobs()

and
nextJob()
.
Together, those methods make up an
iterator
. When an
Employee

object initializes,
an internal index in the private
jobs

array sets to zero. The
hasMoreJobs()

method returns a Bool
ean true value if that index's value is less than the
jobs

array's length.
nextJob()

uses that index's value to return a
Job

object from the
array at that index
--

and increments that index's value so the next call to
nextJob()

returns a reference to the n
ext
Job

object.

JobIterator1

does feature problems. First, you can't restart an iteration after it
completes. However, you could easily solve that problem by adding some sort of
reset()

method to
Employee

that assigns 0 to
jobIndex
. A second, more serious

problem is the inability to create multiple iterators for a single
Employee

object.
That problem arises from
hasMoreJobs()

and
nextJob()

being hardcoded within
Employee
. To overcome both problems, developers typically declare an iterator class
whose objec
ts iterate over the
jobs

array. Once an iterator finishes, a program can
start a new iteration by creating a new iterator object. Also, by creating multiple
iterator objects, a program can perform multiple iterations over the same
Employee

object's
jobs

ar
ray. Listing 2 demonstrates the use of an iterator class named
JobIterator
:

Listing 2. JobIterator2.java


// JobIterator2.java


class Job

{



private String jobTitle;




Job (String jobTitle)



{


this.jobTitle = jobTitle;



}




public String t
oString ()



{


return jobTitle;



}

}


class Employee

{



private String name;



private Job [] jobs;




Employee (String name, Job [] jobs)



{


this.name = name;


this.jobs = jobs;



}




String getName ()



{


return name;



}




JobIterator getJobIterator ()



{


return new JobIterator (jobs);



}

}


class JobIterator

{



private Job [] jobs;



private int jobIndex = 0;




JobIterator (Job [] jobs)



{


this.jobs = jobs;



}




boolean hasMoreJobs ()



{


return jobIndex < jobs.length;



}




Job nextJob ()



{


return !hasMoreJobs () ? null : jobs [jobIndex++];



}

}


class JobIterator2

{



public static void main (String [] args)



{


Job [] jobs = { new Job ("Janitor"), new Job ("
Delivery
Person") };



Employee e = new Employee ("John Doe", jobs);



System.out.println (e.getName () + " works the following
jobs:
\
n");



JobIterator ji = e.getJobIterator ();



while (ji.hasMoreJobs ())



System.out.println (
ji.nextJob ());



}

}

JobIterator2

produces the same output as
JobIterator1
, but the source code
differs in that
JobIterator2

migrates iterator code from
Employee

to
JobIterator
. Also,
Employee

declares a
getJobIterator()

method that returns
a reference t
o a new
JobIterator

object. Notice that
JobIterator

and
Employee

are tightly coupled classes:
JobIterator
's constructor requires a reference to
Employee
's private
jobs

array. Keep that coupling in mind, as it offers a clue as to
how class nesting works at
an internal level (for more information on class nesting
internals see the
Inner Classes Specification

document).

Although
JobIterator2

conveniently solves
JobIterator1
's proble
ms, the new
program introduces a new problem: the addition of a new
JobIterator

class at the
same level as
Employee

prevents the insertion of a future generic
JobIterator

interface at that same level into the source file. After all, you cannot have two
cla
sses/interfaces with the same name at the same level in a source file. Although
not a serious problem in our trivial example, in significant programs, situations arise
where introducing classes/interfaces with the same names into the same source file
is ne
cessary. To make those names coexist, you must recognize that some classes
completely depend on other classes. You should be able to declare those dependent
classes within the classes they depend on. Listing 3 shows how to declare a
dependent
JobIterator

c
lass within an
Employee

class
--

upon which
JobIterator

depends:

Listing 3. JobIterator3.java


// JobIterator3.java


class Job

{



private String jobTitle;




Job (String jobTitle)



{


this.jobTitle = jobTitle;



}




public String toString ()



{


return jobTitle;



}

}


class Employee

{



private String name;



private Job [] jobs;




Employee (String name, Job [] jobs)



{


this.name = name;


this.jobs = jobs;



}




String getName ()



{


return name;



}




Jo
bIterator getJobIterator ()



{


return new JobIterator ();



}




class JobIterator



{


private int jobIndex = 0;



public boolean hasMoreJobs ()


{



return jobIndex < jobs.length;


}



public Object nextJob ()



{



return !hasMoreJobs () ? null : jobs [jobIndex++];


}



}

}


class JobIterator3

{



public static void main (String [] args)



{


Job [] jobs = { new Job ("Janitor"), new Job ("Delivery
Person") };



Employee e = new Emplo
yee ("John Doe", jobs);



System.out.println (e.getName () + " works the following
jobs:
\
n");



Employee.JobIterator eji = e.getJobIterator ();



while (eji.hasMoreJobs ())



System.out.println (eji.nextJob ());



}

}

JobIterator3
, w
hich produces the same output as
JobIterator1

and
JobIterator2
, illustrates class nesting: class
Employee

contains the class
JobIterator

declaration. That nesting results in
JobIterator

not requiring a
constructor because
JobIterator

can directly access
Em
ployee
's private
jobs

field. Also, that nesting requires
JobIterator3
's
main()

method to prefix
Employee.

to
JobIterator

whenever
main()

needs access to
JobIterator
.
Because
JobIterator3
's
JobIterator

class no longer requires a constructor or its
own
jobs

field, the source code is somewhat clearer than
JobIterator2
's source
code.

In addition to improved source code clarity, class nesting features a second benefit.
When classes nest within other classes, name conflicts reduce. Look carefully at
Listing 3. T
he top
-
level classes are
Job
,
Employee
, and
JobIterator3
. There is
also an
Employee.JobIterator

class. If we choose to insert a
JobIterator

interface at the same level as
Employee
, we would end up with
Job
,
Employee
,
JobIterator3
, and
Employee.JobIterator

classes, and a
JobIterator

interface. Because
Employee.JobIterator

and
JobIterator

represent two
different names, no name conflict results.

Note


If you compile
JobIterator3

and examine the
resulting class files, you will discover an
Employee$JobIterator
.class

filename. That
filename corresponds to the class file containing byte
codes for the nested
JobIterator

class within
Employee
. Notice the dollar sign character. Java's
compiler generates names for nested classes by prefixing
their enclosing class nam
es and dollar sign characters to
nested class names. Why use a dollar sign? It is a
legitimate character that underlying platforms allow for
filenames. In contrast, period characters (
.
) separate the
actual name from the file's extension, and are not alway
s
valid for use in platform
-
specific filenames.

What nested classes does Java support?

Java divides nested classes into two main categories: nested top
-
level classes and
inner classes. Furthermore, Java divides inner classes into the instance inner clas
s,
local inner class, and anonymous inner class categories. To understand nested
classes, you need to understand each category. We begin exploring those categories
by focusing on nested top
-
level classes.

Nested top
-
level classes

When you declare a class

outside any other class, Java regards that class as a
top
-
level class
. If you declare a class within a top
-
level class and prefix keyword
static

to the nested class declaration, you end up with a
nested top
-
level class
. The
following code fragment demonst
rates a top
-
level class and a nested top
-
level class:

class TopLevelClass

{



static class NestedTopLevelClass



{



}

}

Just as a static field and a static method (also known as a class field and a class
method) are independent of any objects that you

create from their enclosing classes,
a nested top
-
level class is also independent from such objects. Consider the following
code fragment:

class TopLevelClass

{



static int staticField;



int instanceField;




static class NestedTopLevelClass



{



static


{



System.out.println ("Can access staticField " +
staticField);

//


System.out.println ("Cannot access instanceField " +
instanceField);


}



{



System.out.println ("Can access staticField " +
staticField)
;

//


System.out.println ("Cannot access instanceField " +
instanceField);


}



}

}

Within the class block and object block initializers of the preceding code fragment's
NestedTopLevelClass

class, you can access
TopLevelClass
's
staticField

var
iable. However, you cannot access
instanceField

from within either initializer.
Because
NestedTopLevelClass

cannot access
TopLevelClass
's
instanceField

variable,
NestedTopLevelClass

is independent of any
TopLevelClass

objects.

Caution


A nested top
-
level

class cannot access any enclosing
class's instance members (fields and methods).

Although
NestedTopLevelClass

cannot access
TopLevelClass
's instance fields,
keyword
static

does not prevent
NestedTopLevelClass

from declaring its own
instance fields or cr
eating
NestedTopLevelClass

objects. For proof, check out
Listing 4:

Listing 4. NestedTopLevelClassDemo.java


// NestedTopLevelClassDemo.java


class TopLevelClass

{



static class NestedTopLevelClass



{


int myInstanceField;



NestedTopLevelCl
ass (int i)


{



myInstanceField = i;


}



}

}


class NestedTopLevelClassDemo

{



public static void main (String [] args)



{


TopLevelClass.NestedTopLevelClass ntlc;


ntlc = new TopLevelClass.NestedTopLevelClass (5);


S
ystem.out.println (ntlc.myInstanceField);



}

}

When run,
NestedTopLevelClassDemo

produces the following output:

5

NestedTopLevelClassDemo
's
main()

method creates a
NestedTopLevelClass

variable
--

ntlc
. The syntax for declaring that variable is identical

to Listing 3's
Employee.JobIterator eji = e.getJobIterator ();

syntax. Generally,
when you need a nested class type variable, prefix the nested class type's name with
the names of all enclosing classes and period separators. The same idea holds when
ident
ifying the nested class type after keyword
new
: you must supply the names of
all enclosing classes and period separators prior to the nested class name.

At this point, you might wonder whether you can declare a nested top
-
level class
within a nested top
-
l
evel class. Also, what happens if two different enclosing classes
declare the same field variable name with different types and/or initial values? For an
answer to both questions, check out Listing 5:

Listing 5. NestingAndShadowingDemo.java


// NestingAnd
ShadowingDemo.java


class TopLevelClass

{



private static int a = 1;



private static int b = 3;




static class NestedTopLevelClass



{


private static int a = 2;



static class NestedNestedTopLevelClass


{



void printFields ()



{


System.out.println ("a = " + a);


System.out.println ("b = " + b);



}


}



}

}


class NestingAndShadowingDemo

{



public static void main (String [] args)



{


TopLevelClass.NestedTopLevelClass.Nested
NestedTopLevelClass
nntlc;


nntlc = new TopLevelClass.NestedTopLevelClass.


NestedNestedTopLevelClass ();


nntlc.printFields ();



}

}

When run,
NestingAndShadowingDemo

produces the following output:

a = 2

b = 3

T
he fact that
NestingAndShadowingDemo

compiles and runs proves that you can
nest nested top
-
level classes within nested top
-
level classes. Also, the output shows
that
NestedTopLevelClass
's
a

field shadows
TopLevelClass
's
a

field. As a
result, the contents o
f
NestedTopLevelClass
's
a

field (2) prints.

Nested top
-
level classes prove inadequate for accessing an enclosing class's instance
fields or calling the class's instance methods. To accommodate instance member
access, Java supports
inner classes
. Inner cla
sses resemble nested top
-
level classes,
except that you never declare an inner class with the
static

keyword. We now
examine inner classes, beginning with the instance inner class category.

Tip


You can declare a nested top
-
level class with the
private
,
protected
, or
public

keywords to
determine that class's level of accessibility to code
outside the enclosing class.

Instance inner classes

Suppose you declare a class within another class and omit the
static

keyword from
that nested class's declaration.

Instead of ending up with a nested top
-
level class,
you end up with an
instance inner class
. Unlike nested top
-
level classes, which can
access only enclosing classes' static members, instance inner classes can access both
static and instance members. For
an example of an instance inner class, refer to
Listing 3. Within class
Employee
, you see instance inner class
JobIterator
. Look
carefully at both classes and you see that
JobIterator

can access
Employee
's
private
jobs

instance field.

Tip


You can declar
e an instance inner class with the
private
,
protected
, or
public

keywords to
determine that class's level of accessibility to code
outside the enclosing class.

Local inner classes

In addition to allowing class nesting within classes, Java also allows yo
u to nest
classes even closer to where they are needed by placing a class in any
block,

a
region of code that appears between open/close brace characters (
{ }
). That
placement implies that the class can appear inside a method declaration and even
between a

pair of brace characters that follow an
if

statement. Although the class
still nests within some enclosing class, the nested class is now situated much closer
to the code that needs to work with that class. Such a class is known as a
local inner
class
.

L
ocal inner classes have one advantage over instance inner classes. In addition to
being able to access enclosing classes' instance and class fields (and call the instance
and class methods), local inner classes can access local variables and/or a method's
parameters. Examine Listing 6 to see a local inner class:

Listing 6. LocalInnerClassDemo.java


// LocalInnerClassDemo.java


import java.util.*;


class ComputerLanguage

{



private String name;




ComputerLanguage (String name)



{


this.name = nam
e;



}




public String toString ()



{


return name;



}

}


class LocalInnerClassDemo

{



public static void main (String [] args)



{


ComputerLanguage [] cl =


{



new ComputerLanguage ("Ada"),



new ComputerLanguage
("Algol"),



new ComputerLanguage ("APL"),



new ComputerLanguage ("Assembly
-

IBM 360"),



new ComputerLanguage ("Assembly
-

Intel"),



new ComputerLanguage ("Assembly
-

Mostek"),



new ComputerLanguage ("Assembly
-

Moto
rola"),



new ComputerLanguage ("Assembly
-

VAX"),



new ComputerLanguage ("Assembly
-

Zilog"),



new ComputerLanguage ("BASIC"),



new ComputerLanguage ("C"),



new ComputerLanguage ("C++"),



new ComputerLanguage

("Cobol"),



new ComputerLanguage ("Forth"),



new ComputerLanguage ("Fortran"),



new ComputerLanguage ("Java"),



new ComputerLanguage ("LISP"),



new ComputerLanguage ("Logo"),



new ComputerLanguage ("Modula 2
"),



new ComputerLanguage ("Pascal"),



new ComputerLanguage ("Perl"),



new ComputerLanguage ("Prolog"),



new ComputerLanguage ("Snobol")


};



Enumeration e = enumerator ((Object []) cl);



while (e.hasMoreEle
ments ())



System.out.println (e.nextElement ());



}




static Enumeration enumerator (final Object [] array)



{


class LocalInnerClass implements Enumeration


{



private int index = 0;




public boolean hasMoreElemen
ts ()



{


return index < array.length;



}




public Object nextElement ()



{


return array [index++].toString ();



}


}



return new LocalInnerClass ();



}

}

When run,
LocalInnerClass
Demo

produces the following output:

Ada

Algol

APL

Assembly
-

IBM 360

Assembly
-

Intel

Assembly
-

Mostek

Assembly
-

Motorola

Assembly
-

VAX

Assembly
-

Zilog

BASIC

C

C++

Cobol

Forth

Fortran

Java

LISP

Logo

Modula 2

Pascal

Perl

Prolog

Snobol

LocalInnerClassDe
mo

demonstrates the declaration of local inner class
LocalInnerClass

within
LocalInnerClassDemo
's
enumerator()

class method.
Notice that
enumerator()

returns a reference to an object whose class implements
the
Enumeration

interface (located in the
java.uti
l

package). Because
LocalInnerClass

also implements
Enumeration
,
enumerator()

can legally
return a reference to a
LocalInnerClass

object.

A careful examination of
enumerator()
's parameter list reveals a single
array

parameter of type
Object []
, and someth
ing else
--

keyword
final
. What is the
purpose of keyword
final
? The "Inner Classes Specification" document offers a
somewhat cryptic answer:

Because of potential synchronization problems, there is by design no
way for two objects to share access to a cha
ngeable local variable.

The quote above implies that a nonfinal (that is, changeable) local variable (or
parameter), which code within two objects
--

a local inner class object and the object
whose instance method declares a local inner class
--

accesses,
can possibly cause
synchronization problems.
LocalInnerClassDemo

supplies a
LocalInnerClass

object and the
enumerator()

class method, which both share the nonfinal
array

parameter. This is a different scenario than the quote describes.

What kinds of synch
ronization problems arise? One possibility: Inconsistencies arise
when multiple threads attempt simultaneous access to a shared variable. (I discuss
this possibility in a future article that explores synchronization.) Another possibility,
to which I believ
e the quote refers: A local inner class modifies a nonfinal local
variable, but that modification does not appear later in the method, as the following
(illegal) code fragment clarifies:

void someMethod (int x)

{




// Assume x contains 20




class Fre
d



{


Fred ()


{



x = 30; // Compiler reports error


}



}




Fred f = new Fred ();




System.out.println (x); // x still contains 20




return x;

}

The code fragment above introduces the
someMethod()

instance method with a
sin
gle parameter
x
, which initially contains 20. Local inner class
Fred
's constructor
assigns 30 to
x

(which the compiler does not allow, but let's assume otherwise).
However,
System.out.println (x);

reveals
x

to contain 20
--

not 30. Talk about
confusion! To

allow
Fred

access to
x
, the compiler copies
x

within
Fred
.
Fred
's
constructor modifies the copy
--

not the original
x
. A new and unwary Java developer
probably would not know that two copies of
x

exist and expect
x

to contain 30 after
Fred f = new Fred ()
;

executes. Discovering this incorrect assumption leads to
confusion. To avoid this confusion, Sun forces developers to prefix the
int x

parameter declaration with keyword
final
:
void someMethod (final int x)
.
As a result,
Fred()
's
x = 30;

statement is ill
egal.

Note


If the local variable or parameter has primitive type, as
opposed to reference type, the compiler simply
substitutes a literal for that variable.

Can
LocalInnerClassDemo

access
array

after
enumerator()

returns? After all,
doesn't a paramete
r (final or otherwise) disappear once a method exits? Yes, the
parameter disappears; however, if you examine the byte codes in the appropriate
class files, you will discover that the compiler performs some sleight
-
of
-
hand code
generation. First, the compil
er creates a
LocalInnerClass(Object val$array)

constructor in
LocalInnerClass
. Second, the compiler creates a hidden
Object
val$array;

field within
LocalInnerClass
. That field is known as a
synthetic field

because the compiler creates it. Finally, the comp
iler changes
return new
LocalInnerClass ();
, within
enumerator()
, to
return new
LocalInnerClass (array);
. Because
LocalInnerClass

has a copy of the
reference to the same
Object []

array, as passed to
enumerator()
,
LocalInnerClass
's methods can refer to tha
t array long after
enumerator()
's
array

parameter disappears.

Tip


Learn more about the compiler's manipulation of local
inner classes into top
-
level classes by studying the
Inn
er
Classes Specification

document and by using the javap
program to disassemble class files.

Anonymous inner classes

When a class is short, you'll want to declare a local inner class without a name
because that name won't contribute any meaning to the
class, Also, by not choosing
a name, you reduce the chance of name conflicts when several local inner classes
appear within the same enclosing class. A local inner class that lacks a name is
known as an
anonymous inner class
.

Because an anonymous inner cl
ass lacks a name, you declare that class where you
create an object from that class, as Listing 7 demonstrates:

Listing 7. AnonymousInnerClassDemo1.java


// AnonymousInnerClassDemo1.java


abstract class Farmer

{



protected String name;




Farmer (Strin
g name)



{


this.name = name;



}




abstract void occupation ();

}


class BeefFarmer extends Farmer

{



BeefFarmer (String name)



{


super (name);



}




void occupation ()



{


System.out.println ("Farmer " + name + " raises beef

cattle");



}

}


class AnonymousInnerClassDemo1

{



public static void main (String [] args)



{


BeefFarmer bf = new BeefFarmer ("John Doe");


bf.occupation ();



new Farmer ("Jane Doe")


{


void occupation ()



{



System.out.println ("Farmer " + name + " milks
cows");


}


}.occupation ();



}

}

When run,
AnonymousInnerClassDemo1

produces the following output:

Farmer John Doe raises beef cattle

Farmer Jane Doe milk
s cows

AnonymousInnerClassDemo1

declares an abstract
Farmer

class that encapsulates
a farmer's name and occupation
--

via an abstract
occupation()

method. A
BeefFarmer

class extends
Farmer

and overrides
occupation()

to identify what a
beef farmer does. Tha
t class appears in
AnonymousInnerClassDemo1
's
main()

method, which creates a
BeefFarmer

object and calls its
occupation()

method to
print out the beef farmer's occupation. No surprises here! However, as you continue
to examine
main()
, you encounter somethi
ng strange:
new Farmer ("Jane
Doe") { ... }
. It appears that we are trying to create an object from the abstract
Farmer

class, which is impossible. But what does that open brace character
following
("Jane Doe")

mean? Surely that is not legal Java code? In
actual fact,
the code is legal and reads like this: "Have the JVM create an object from a
Farmer

anonymous subclass." That subclass overrides
Farmer
's
occupation()

method and
calls the
Farmer (String name)

constructor to initialize
Farmer
's protected
name

field. After creating that object, call its
occupation()

method and then discard the
object's reference so the object becomes eligible for garbage collection.

AnonymousInnerClassDemo1
's use of a superclass constructor to initialize the
resulting object's
superclass layer begs the following question: can I declare my own
constructors in an anonymous class? The answer is no. Because a constructor
requires a class name and because an anonymous class has no name, how could the
compiler choose a name?

Caution


Attempts to declare constructors in an anonymous inner
class fail because constructors must have the names of
the classes in which they appear and anonymous inner
classes have no names.

Instead of constructors, you can use an object block initializer t
o perform custom
initialization when creating an object from an anonymous inner class. For example,
suppose you want to customize the
Farmer Jane Doe milks cows

message in
AnonymousInnerClassDemo1
's anonymous subclass of
Farmer
. You want to pass
the number

of cows to be milked on the command line and have that value appear in
the message. Because an object block initializer executes during object creation, you
simply perform the appropriate command line argument initialization in an object
block initializer
, as Listing 8 demonstrates:

Listing 8. AnonymousInnerClassDemo2.java


// AnonymousInnerClassDemo2.java


abstract class Farmer

{



protected String name;




Farmer (String name)



{


this.name = name;



}




abstract void occupation ();

}


class

BeefFarmer extends Farmer

{



BeefFarmer (String name)



{


super (name);



}




void occupation ()



{


System.out.println ("Farmer " + name + " raises beef
cattle");



}

}


class AnonymousInnerClassDemo2

{



public static void main (fin
al String [] args)



{


BeefFarmer bf = new BeefFarmer ("John Doe");


bf.occupation ();



new Farmer ("Jane Doe")


{


private String count;



{



if (args.length == 1)



c
ount = args [0];


}



void occupation ()


{



if (count == null)



System.out.println ("Farmer " + name + "
milks cows");



else



System.out.println

("Farmer " + name + "
milks " +



count + " cows");


}


}.occupation ();



}

}

Assuming you type
java AnonymousInnerClassDemo2 10

at the command line,
you receive the following output:

Farmer
John Doe raises beef cattle

Farmer Jane Doe milks 10 cows

AnonymousInnerClassDemo2

still initializes the resulting object's
Farmer

layer, by
calling constructor
Farmer (String name)

with
Jane Doe

as the object value that
name

references. However, the resul
ting object's anonymous layer also has a chance
to initialize, through the object block initializer.

Note


Even though an anonymous inner class has no name, the
compiler still needs to generate a name for a class file. It
turns out that the compiler choo
ses integer numbers,
which append to enclosing class names and dollar sign
characters, for anonymous inner class names. For
example, in
AnonymousInnerClassDemo2
, the
compiler generates
AnonymousInnerClassDemo2$1.class

as
the class file name for the anonymo
us inner class.

Before you leave this section, consider the following practical application of
anonymous inner classes: Developers often use anonymous inner classes to simplify
event
-
handling
--

that is, notifications of significant activities, such as m
oving the
mouse or pressing a key
--

in programs that generate and display graphical user
interfaces (GUIs). Using an anonymous inner class for event handlers proves
convenient because they often do not require class names. Listing 9 presents an
example wh
ere an anonymous inner class facilitates the handling of the window
-
closing event:

Listing 9. AnonymousInnerClassDemo3.java


// AnonymousInnerClassDemo3.java


import java.awt.*;

import java.awt.event.*;


class AnonymousInnerClassDemo3

{



public static v
oid main (String [] args)



{


// Create a rectangular frame window with a title bar at
the top.



Frame f = new Frame ("Anonymous Inner Class Demo #3");



// Add a window listener that will generate a window
closing event


// in respo
nse to user attempts to click the little X
button (on


// Windows platforms) to the right of the title bar. When
the user


// clicks that button, the window closing event results in
a call


// to a method named windowClosing().

By calling
S
ystem.exit (0);


// from within that method, the application exits.



f.addWindowListener (new WindowAdapter ()



{



public void windowClosing
(WindowEvent e)



{


System.exit (0);



}



});



// Establish the frame window's size as 300 horizontal
pixels by


// 100 vertical pixels.



f.setSize (300, 100);



//

Display the frame window and get the underlying event
handling


// system running.



f.setVisible (true);



}

}

When run,
AnonymousInnerClassDemo3

displays a rectangular window, known as
a frame window, because it is the resulting GUI's main wi
ndow. Once the frame
window appears, however, the user should be able to remove that window and stop
AnonymousInnerClassDemo3
's execution. That termination typically occurs in
response to the user clicking a little button, labeled
X,

to the top right of th
e
window's title bar
--

at least on Windows platforms.

When the user clicks X, the underlying Java windowing toolkit creates an event
object and calls a special method
--

windowClosing (WindowEvent e)
, where
e

contains a reference to that event object
--

in an object known as the frame
window's window listener. That listener object registers with the underlying toolkit
(so the toolkit knows the method location) by calling
Frame
's
addWindowListener
(WindowListener wl)

method.

The
WindowListener

interface d
eclares several methods, one method for each
potential window event. To spare the developer from having to supply code for all
those methods, Java's windowing toolkit designers built a
WindowAdapter

class that
implements all
WindowListener

methods. Because

those implemented methods are
empty stubs, the developer declares an anonymous inner class that extends
WindowAdapter

and overrides one or more of those methods with specific code. As
you can see, I have chosen to override
windowClosing (WindowEvent e)

an
d
have that method call
System.exit (0);

to terminate the program (which also
closes the window), in response to a call made by the windowing toolkit to
windowClosing (WindowEvent e)
.

Note


You will often work with anonymous inner classes when
developing

event handlers for your GUIs. In future
articles, I will provide many examples of event handlers
built from anonymous inner classes.

Review

This article has shown class nesting to be a useful addition to the Java language. Not
only does class nesting h
elp clarify source code
--

because you can declare classes
closer to the objects they manipulate
--

it also helps reduce the number of conflicts
between the names of classes declared at the same level within a source file.

There are four categories of nes
ted classes: nested top
-
level classes, instance inner
classes, local inner classes, and anonymous inner classes. Nested top
-
level classes
can only access the class fields and call the class methods in any enclosing class. In
contrast, instance inner classe
s can access an enclosing class's class and instance
fields, and call an enclosing class's class or instance methods. Because nested top
-
level classes and instance inner classes can only appear within the confines of
another class, Java supplies local inne
r classes that can appear within any block
--

including a method block or the block following an
if

statement. Furthermore,
because some local inner classes are so short they do not warrant their own class
names, Java supplies anonymous inner classes. Loca
l and anonymous inner classes
can access the local variables and parameters accessible within their enclosing
blocks, provided that you mark those local variables and parameters final.

I encourage you to
email me

with any questions you might have involving either this
or any previous article's material. (Please, keep such questions relevant to material
discussed in this column's articles.) Your questions and my answers will appear in
the relevant study guides.

In
next month's article, you will learn about exceptions and how to handle them.


About the author

J
eff Friesen

has been involved with computers for the past 20 years. He holds a
degree in computer science and has worked with many computer languages. Jeff has
also taught introductory Java programming at the college level. In addition to writing
for
Java
World,

he has written his own Java book for beginners
--

Java 2 By Example,
Second Edition

(Que Publishing, 2001; ISBN: 0789725932)
--

and helped write
Special Edition Using Java 2 Platform

(Que Publishing, 2001; ISBN: 0789724685).
Jeff goes by the nicknam
e Java Jeff (or JavaJeff). To see what he's working on, check
out his Website at
http://www.javajeff.com
.

Resources




Download this article's source code and resource files:

http://www.javaworld.com/javaworld/jw
-
02
-
2002/java101/jw
-
0201
-
java101.zip



For a glossary specific to this article, homework, tips, and more, see the
Java
101

study guide that accompanies this article:

http://www.javaworld.com/javaworld/jw
-
02
-
2002/jw
-
0201
-
java101guide.html



The Java Language Specification, Second Edition,

James Gosling, Bill Joy, Guy
Steele, Gilad Bracha
(Sun Microsystems, 2000):

http://www.javasoft.com/docs/books/jls/second_edition/html/j.title.doc.html



The Java Tutorial,

Mary Campione, Kathy Walrath, Alison Hum
l (Addison
-
Wesley, 2000; ISBN: 0201703939):

http://www.javasoft.com/tutorial/



Sun's official Java language definition:

http://j
ava.sun.com/docs/overviews/java/java
-
overview
-
1.html



Read Sun's "Inner Classes Specification" (February 1997) to learn more about
nested top
-
level classes and inner classes:

http://java.sun.com/products/jdk/1.1/docs/guide/innerclasses/spec/innerclas
ses.doc.html



Review Jeff's previous column, "Trash Talk, Part 2" (
JavaWorld,

January
2002):

http://www.javaworld.com/javaworld/jw
-
01
-
2002/jw
-
0104
-
java101.html



Go on to Jeff's next
Java 101

lesson: "Exceptions to the Programming Rules,
Part 1" (
JavaWorld,

March 2002):

http://www.javaworld.com/javaworld/jw
-
03
-
2002/jw
-
0301
-
java101.html



Check out past
Java 101

articles:

http://www.javaworld.com/javaworld/topicalin
dex/jw
-
ti
-
java101.html