6.3 Internal class
Internal class category is defined in another class. Why would you do that? There are two reasons:
- Inner classes can be hidden to other classes in the same package.
- Method within the class can access data from the definition of the scope thereof, including those originally private data.
Inner classes in the past is very important to implement a callback succinctly, but now lambda expressions better. However, inner classes are useful for building code. The following sections will guide you through all the details.
C ++ Note
Nested C ++ classes. Nested classes included within the scope of the enclosing class. Here is a typical example: objectlist defines a class used to store a link, and a position to define the iterator class.
class LinkedList { public: class Iterator // a nested class { public: void insert(int x); int erase(); . . . private: Link* current; LinkedList* owner; }; . . . private: Link* head; Link* tail; };
Similar to the nested class in Java within the class. However, Java inner classes have an additional feature to make them richer and more useful than nested classes in C ++. Objects from within the classes have implicit references to instances of it external object. With this pointer, it can access the total status of the external object. For example, in Java, the
Iterator
class does not point to it pointsLinkedList
explicit pointers.In Java,
static
an internal pointer to the class does not have this addition. They are C ++, Java analogy nested class.
6.3.1 Access class internal object state
The syntax of inner classes is quite complicated. For this reason, we offer a simple but somewhat contrived example to demonstrate the use of inner classes. We reconstructed Timertest
the example and extract the TalkingClock
class. A sound clock consists of two parameters: a gap opening and closing flag or notification between beeps.
public class TalkingClock
{
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) { . . . }
public void start() { . . . }
public class TimePrinter implements ActionListener
// an inner class
{
. . .
}
}
Please note that TimePrinter
class is now in TalkingClock
class. This does not mean that each TalkingClock
has an TimePrinter
instance field. As you will see, TimePrinter
the object is the TalkingClock
constructor of the class.
The following is a more detailed TimePrinter
class. Please note that the actionPerformed
method checks before beeps beep
flag.
public class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
Amazing thing happened. TimePrinter
No class named beep
instance variables or fields. In contrast, beep
it is referring to the creation TimePrinter
of TalkingClock
the object field. As you can see, the internal class methods can access their own data fields and create data fields its external objects.
To achieve this, the object of an inner class always get to create its implicit reference object (see Figure 6.3).
Figure 6.3 having an internal class object reference to an external class object.
This reference is not visible in the definition of internal class. However, in order to illustrate this concept, let's call it an external object. Then, the actionPerformed
method is equivalent to the following:
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (outer.beep) Toolkit.getDefaultToolkit().beep();
}
External reference type provided constructor. The compiler modifies all internal class constructor, a parameter is added as an external reference type. TimePrinter
Class does not define a constructor; therefore, no compiler synthesized a constructor parameter, generate the following code:
public TimePrinter(TalkingClock clock) // automatically generated code
{
outer = clock;
}
Please note that outer
not a Java keyword. We only use it to illustrate the internal mechanism of a class involved.
When the start
constructor TimePrinter
object, the compiler will this
reference clock transmitted to the current session to the constructor:
var listener = new TimePrinter(this); // parameter automatically added
Listing 6.7 shows the complete test procedure within the class. Look at access control. If the TimePrinter
class is a regular class, it will need TalkingClock
public access to the class beep
flag. Inner classes is an improvement. It is only required to provide access to another class of interest.
note
We can
TimePrinter
declare that classprivate
. Then, the onlyTalkingClock
method to constructTimePrinter
objects. Only inner classes can be private. General class always has a bag or public access.
清单6.7 innerClass/InnerClassTest.java
package innerClass;
import java.awt.*;
import java.awt.event.*;
import java.time.*;
import javax.swing.*;
/**
* This program demonstrates the use of inner classes.
* @version 1.11 2017-12-14
* @author Cay Horstmann
*/
public class InnerClassTest
{
public static void main(String[] args)
{
var clock = new TalkingClock(1000, true);
clock.start();
// keep program running until the user selects "OK"
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
/**
* A clock that prints the time in regular intervals.
*/
class TalkingClock
{
private int interval;
private boolean beep;
/**
* Constructs a talking clock
* @param interval the interval between messages (in milliseconds)
* @param beep true if the clock should beep
*/
public TalkingClock(int interval, boolean beep)
{
this.interval = interval;
this.beep = beep;
}
/**
* Starts the clock.
*/
public void start()
{
var listener = new TimePrinter();
var timer = new Timer(interval, listener);
timer.start();
}
public class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
}
Special inner class syntax rules 6.3.2
In the previous section, we have an in-house by calling the class outer
to explain its outer class reference. In fact, the external reference the correct syntax is more complicated. expression
OuterClass.this
It represents an external class references. For example, the TimePrinter
internal class actionPerformed
method written as
public void actionPerformed(ActionEvent event)
{
. . .
if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}
Instead, you can use the syntax to write more explicit internal object constructor.
outerObject.new InnerClass(construction parameters)
ActionListener listener = this.new TimePrinter();
Here, new structure of TimePrinter
external object references are set to create a method of the class object internal this
reference. This is the most common situation. As this
usual, . Qualifier is redundant. However, by explicitly naming the external reference type to another object. For example, since TimePrinter
a common internal class, it can be any sound a clock configured TimePrinter
:
var jabberer = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();
Please note that you will be called an inner class
OuterClass.InnerClass
When it occurs outside the range of the external type.
note
Any internal static fields declared in the class must be final field, and initialized with compile-time constant. If the field is not constant, it may not be unique.
Inner classes can not have
static
a method. Java language specification gives no reason for this restriction. Static methods may be allowed to access only the static fields and methods of the enclosing class. Obviously, the complexity of the language designers believe that outweigh the benefits.
6.3.3 inner classes useful? Really necessary? Safety?
When the Java language in the inner class is added to Java 1.1, many programmers believe they are one of the main new features of the Java language embodies simpler than C ++. Admittedly, inner class syntax is very complex. (As we study anonymous inner classes later in this chapter, it becomes more complicated.) How do inner classes and other language features (such as access control and security) interaction is not obvious.
By adding features an elegant and interesting rather than needed, Java began a path of destruction to destroy so much of it in other languages?
Although we will not try to fully answer this question, but it is worth noting that class is an internal compiler phenomenon, rather than a virtual machine. Inner classes are converted to regular class files, separated by external and internal class name with a $ (dollar sign), and a virtual machine without any special knowledge of them.
For example, TalkingClock
the class of TimePrinter
the class is converted into TalkingClock$TimePrinter.class
class files. To see this at work, try the following experiment: Run Chapter 5 of the ReflectionTest
program and give it a class TalkingClock$TimePrinter
to reflect. Or, simply use the javap
utility:
javap -private ClassName
note
If you use UNIX, please keep in mind when providing an escape $ character class names on the command line. In other words, run
ReflectionTest
orjavap
use the programjava reflection.ReflectionTest innerClass.TalkingClock\$TimePrinter
or
javap -private innerClass.TalkingClock\$TimePrinter
You will get the following printout:
public class innerClass.TalkingClock$TimePrinter
implements java.awt.event.ActionListener
{
final innerClass.TalkingClock this$0;
public innerClass.TalkingClock$TimePrinter(innerClass.TalkingCloc
public void actionPerformed(java.awt.event.ActionEvent);
}
You can clearly see the compiler to generate an additional instance field, this$0
used to refer to the outer class. ( this$0
The name is synthesized by the compiler, you can not refer to it in code.) You can also see the constructor TalkingClock
parameters.
If the compiler can automatically perform this conversion, you can not simply manually programming the same mechanism it? Let's give it a try. We will TimePrinter
become a regular class, but not in the TalkingClock
class inside. In the constructor TimePrinter
when the object, we will create its reference to the object passed to it.
class TalkingClock
{
. . .
public void start()
{
var listener = new TimePrinter(this);
var timer = new Timer(interval, listener);
timer.start();
}
}
class TimePrinter implements ActionListener
{
private TalkingClock outer;
. . .
public TimePrinter(TalkingClock clock)
{
outer = clock;
}
}
Now let's look at actionPerformed
the method. It needs to access outer.beep
.
if (outer.beep) . . . // ERROR
Here we encounter a problem. Inner class can access private data outside class, but outside of TimePrinter
class can not.
Therefore, inner classes really more powerful than regular classes because they have more access.
If inner classes are converted to regular classes with funny names, then you might want to know how to gain access to the interior of these classes are added - the virtual machine they know nothing about. To solve this mystery, let us re-use ReflectionTest
program monitors TalkingClock
categories:
class TalkingClock
{
private int interval;
private boolean beep;
public TalkingClock(int, boolean);
static boolean access$0(TalkingClock);
public void start();
}
Note that the compiler added to the outer class static access$0
method. It returns as an object passed as a parameter of the beep
field. (Method name may be slightly different, such as access$000
, depending on the compiler.)
Internal class method calls this method. statement
if (beep)
In the TimePrinter
class actionPerformed
methods, effectively following call:
if (TalkingClock.access$0(outer))
This is a security risk? of course. For others, call access$0
methods to read private beep
fields is very easy to do. Of course, access$0
not a legal name of the Java method. But hackers are familiar with the structure of the class file can be easily generated using a virtual machine instruction class file to invoke the method, for example, using a hex editor. Since the private packet access method, it is necessary to attack the same code in the package by the attack class.
All in all, if a private inner class to access a data field, you can add to the outside class package to access the data fields other classes, but to do this requires skill and determination. Programmer can not accidentally get access but must intentionally build or modify the class file for this purpose.
note
Synthesis and methods of construction will be very complicated. (If you are squeamish, skip this note.) Suppose we would
TimePrinter
be converted to a private inner class. The virtual machine is not a private class, so the compiler will generate the next best thing, a package of classes and access to a private constructor has:private TalkingClock$TimePrinter(TalkingClock);
Of course, no one can call the constructor, it is the second constructor has package access:
TalkingClock$TimePrinter(TalkingClock, TalkingClock$1);
This is the first one.
TalkingClock$1
Synthetic only to distinguish this constructor and other constructors.The compiler
TalkingClock
classstart
constructor method call is convertednew TalkingClock$TimePrinter(this, null)
6.3.4 partial inner class
If you look closely TalkingClock
the code example, you will find only need to type TimePrinter
the name of the time: When you start
create objects of that type of approach.
In this case, the class may be locally defined in a single process.
public void start()
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
var listener = new TimePrinter();
var timer = new Timer(interval, listener);
timer.start();
}
Never use local class access specifiers (i.e., public or private) statement. Their scope is always limited to declare their blocks.
Local class has a big advantage: they are completely hidden in the outside world, even TalkingClock
other code in the class can not access them. In addition to start
addition, there is no way to understand TimePrinter
class.
6.3.5 Access variable from outside the method
Local analogy other internal class has another advantage. They can not only access to the outer class field, even access local variables! However, these local variables must be valid final
variables. In other words, once assigned, they may never change.
This is a typical example. Let us interval
and beep
parameters from the TalkingClock
move constructor start
method.
public void start(int interval, boolean beep)
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
var listener = new TimePrinter();
var timer = new Timer(interval, listener);
timer.start();
}
Note that the TalkingClock
class is no longer a need to store beep
instance field. It only reference start
method beep
parameter variables.
Perhaps this is not surprising. This line
if (beep) ...
After all, it is in the final start
process, so why it can not access the beep
value of the variable it?
To understand why there is a delicate question, let us consider more carefully control the flow.
start
Method is called- Object variable
listener
by calling an internal classTimePrinter
constructor initialization. listener
Reference passed toTimer
the constructor, the timer starts,start
the method exits. In this case,start
the method ofbeep
parameter variables no longer exist.- A second later,
actionPerformed
a method to perform `if (beep) ...
For actionPerformed
the code working method, TimePrinter
the class must beep
be the value of the parameter before the disappearance of beep
replication field start
local variable method. This is indeed what happened. In our example, the compiler synthesis named local inner class TalkingClock$1TimePrinter
. If you re-use ReflectionTest
program monitors TalkingClock$1TimePrinter
category, you get the following output:
class TalkingClock$1TimePrinter
{
TalkingClock$1TimePrinter(TalkingClock, boolean);
public void actionPerformed(java.awt.event.ActionEvent);
final boolean val$beep;
final TalkingClock this$0;
}
Please note that the constructor boolean
parameters and val$beep
instance variables. When creating an object, the value beep
is passed to the constructor, and stored in the val$beep
field. The compiler detect access to local variables, instance field generated matches for each variable, and local variable to the copy constructor, to initialize instance fields.
6.3.6 anonymous inner classes
When using local inner classes, you can usually go further. If you want to generate a single object class, the class name need not even. Such a class is called an anonymous inner classes.
public void start(int interval, boolean beep)
{
var listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
var timer = new Timer(interval, listener);
timer.start();
}
This syntax is indeed very mysterious. This means: create an implementation ActionListener
new object interface of the class, where the desired actionPerformed
method is a method defined within the braces.
In general, the syntax is
new SuperType(construction parameters)
{
inner class methods and data
}
Here, an interface may be a parent type, for example ActionListener
; then, the internal class that implements the interface. Supertype may also be a category; then, the internal class extension class.
Anonymous inner classes can not have a constructor, because the name of the constructor must be the same name of the class, and the class has no name. In contrast, the configuration parameters are provided to the constructor of the superclass. In particular, whenever an internal class that implements the interface, it can not have any configuration parameters. However, you must provide a set of parentheses, such as
new InterfaceType()
{
methods and data
}
The difference between the structure of the object's constructor and anonymous inner class, you must carefully review the new object class extensions of the class.
var queen = new Person("Mary");
// a Person object
var count = new Person("Dracula") { . . . };
// an object of an inner class extending Person
Right parenthesis followed by a left brace If the constructor parameter list, an anonymous inner class will be defined.
note
Even if not anonymous class constructors, object initialization block may be provided:
var count = new Person("Dracula") { { initialization } . . . };
Listing 6.8 contains the complete source code for the program Talking Clock with anonymous inner classes of. If you leave this program with a list of 6.7 to compare, you will see in this case, the anonymous inner class solutions is much shorter, and hope through some practice, it can be easily understood.
Over the years, Java programmers usually implement event listeners and other callbacks use anonymous inner classes. Now, it is best to use a lambda expression. For example, a lambda expression can be used more simply as the preparation method of start beginning of this section:
public void start(int interval, boolean beep)
{
var timer = new Timer(interval, event -> {
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
});
timer.start();
}
note
The following initialization technique called double brackets, which uses an internal class syntax. Suppose you want to list an array configuration and passed to a method:
var friends = new ArrayList<String>(); friends.add("Harry"); friends.add("Tony"); invite(friends);
If you no longer need the array list, the best anonymous. But how do you add elements? Methods as below:
invite(new ArrayList<String>() { { add("Harry"); add("Tony"); } });
Note that curly brackets. External braces constitute an anonymous subclass of arraylist. Internal braces is an object initialization block (see chapter 4).
In practice, this technique rarely useful. More likely, invite willing to accept any method List <String>, you can simply pass
List.of("harry", "tony")
.
Be careful
Make an anonymous subclass is often convenient, it and its superclass is almost, but not exactly the same. But you need to be careful using the equals method. In Chapter 5, we recommend that you use the equals method test
if (getClass() != other.getClass()) return false;
Anonymous subclass will not pass this test.
prompt
When you generate a log or debug messages, you usually want to include the name of the current class, for example,
System.err.println("Something awful happened in " + getClass());
But in a static method failed. After all,
getClass
calls, callsthis.getClass()
, and no static methodsthis
. Please use the following expression:new Object(){}.getClass().getEnclosingClass() // gets class of static method
Here,
new Object() {}
generating the anonymous anonymous object subclass of Object,getEnclosingClass
acquiring its closed type, i.e. comprising a class static method.
清单6.8 anonymousInnerClass/AnonymousInnerClassTest.java
package anonymousInnerClass;
import java.awt.*;
import java.awt.event.*;
import java.time.*;
import javax.swing.*;
/**
* This program demonstrates anonymous inner classes.
* @version 1.12 2017-12-14
* @author Cay Horstmann
*/
public class AnonymousInnerClassTest
{
public static void main(String[] args)
{
var clock = new TalkingClock();
clock.start(1000, true);
// keep program running until the user selects "OK"
JOptionPane.showMessageDialog(null, "Quit program?");
System.exit(0);
}
}
/**
* A clock that prints the time in regular intervals.
*/
class TalkingClock
{
/**
* Starts the clock.
* @param interval the interval between messages (in milliseconds)
* @param beep true if the clock should beep
*/
public void start(int interval, boolean beep)
{
var listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.out.println("At the tone, the time is "
+ Instant.ofEpochMilli(event.getWhen()));
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
var timer = new Timer(interval, listener);
timer.start();
}
}
6.3.7 static inner classes
Sometimes, you may want to use an inner class to hide one class inside another class, but do not need the inner class has a reference to the outer class object. It can suppress the generation of the declaration referenced by the inner class static.
Here is a typical example of how you want to do this where it is. Consider computing the minimum and maximum array of tasks. Of course, you can write a method to calculate the minimum value, another method to calculate the maximum. When you call these two methods, the array will be traversed twice. Traversing the array once only be more efficient, while calculating the minimum and maximum.
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}
However, this method must return two numbers. We can kind of two values by defining a contained Pair
to accomplish this:
class Pair
{
private double first;
private double second;
public Pair(double f, double s)
{
first = f;
second = s;
}
public double getFirst() { return first; }
public double getSecond() { return second; }
}
Then the minmax
method may return type of Pair
the object.
class ArrayAlg
{
public static Pair minmax(double[] values)
{
. . .
return new Pair(min, max);
}
}
The caller using the method getFirst
and the getSecond
method to receive the answer:
Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
Of course, the name Pair
is a very common name, in a large-scale project, it is likely that other programmers have the same good idea, but it creates a Pair class contains a pair of strings. We can by ArrayAlg
manipulation Pair
to solve this potential name conflicts become public within the class. Then this class will be exposed as ArrayAlg.Pair
:
ArrayAlg.Pair p = ArrayAlg.minmax(d);
However, different internal class used in the previous example, we do not want Pair
any other object in the object referenced. Internal class can be declared static
to suppress the reference:
class ArrayAlg
{
public static class Pair
{
. . .
}
. . .
}
Of course, only the inner class can be declared static. Internal static type and any other internal classes are identical, but the object is not static inner class reference to its external generated class object. In our example, you must use a static inner classes, because the internal structure of the class object is inside a static method:
public static Pair minmax(double[] d)
{
. . .
return new Pair(min, max);
}
If the Pair
class is not declared static
, the compiler will complain that there is no ArrayAlg
type of implicit objects that can be used to initialize the internal class object.
note
As long as the internal class does not need to access external object, use the static inner classes. Some programmers use the term to describe a nested class static inner classes.
note
Conventional internal class, the class may have a static internal static fields and methods.
note
In the inner class declared inside the interface are automatically static and the public.
6.9 Listing contains ArrayAlg
classes and nested Pair
complete source code for the class.
清单6.9 staticInnerClass/StaticInnerClassTest.java
package staticInnerClass;
/**
* This program demonstrates the use of static inner classes.
* @version 1.02 2015-05-12
* @author Cay Horstmann
*/
public class StaticInnerClassTest
{
public static void main(String[] args)
{
var values = new double[20];
for (int i = 0; i < values.length; i++)
values[i] = 100 * Math.random();
ArrayAlg.Pair p = ArrayAlg.minmax(values);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
}
class ArrayAlg
{
/**
* A pair of floating-point numbers
*/
public static class Pair
{
private double first;
private double second;
/**
* Constructs a pair from two floating-point numbers
* @param f the first number
* @param s the second number
*/
public Pair(double f, double s)
{
first = f;
second = s;
}
/**
* Returns the first number of the pair
* @return the first number
*/
public double getFirst()
{
return first;
}
/**
* Returns the second number of the pair
* @return the second number
*/
public double getSecond()
{
return second;
}
}
/**
* Computes both the minimum and the maximum of an array
* @param values an array of floating-point numbers
* @return a pair whose first element is the minimum and whose second element
* is the maximum
*/
public static Pair minmax(double[] values)
{
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min, max);
}
}