Nested classes
In this section, you learn about nested classes and where and how to use them.
Where to use nested classes
As its name suggests, a nested class (or inner class) is a class defined within another class:
public class EnclosingClass { . . . public class NestedClass { . . . } } |
Like part factors and techniques, Java classes can likewise be characterized at any extension including open, private, or ensured. Settled classes can be helpful when you need to deal with inner preparing inside your class in a question arranged design yet restrict the usefulness to the class where you require it.
Regularly, you utilize a settled class when you require a class that is firmly combined with the class in which it's characterized. A settled class approaches the private information inside its encasing class, yet this structure conveys with it reactions that aren't evident when you begin working with settled classes.
Scope in nested classes
Because a nested class has scope, it's bound by the rules of scope. For example, a member variable can only be accessed through an instance of the class (an object). The same is true of a nested class.
Suppose you have the following relationship between a
Manager
and a nested class called DirectReports
, which is a collection of the Employee
s that report to that Manager
:public class Manager extends Employee { private DirectReports directReports; public Manager() { this.directReports = new DirectReports(); } . . . private class DirectReports { . . . } } |
Similarly as every Manager protest speaks to a one of a kind individual, the DirectReports question speaks to an accumulation of real individuals (representatives) who answer to an administrator. DirectReports vary from one Managerto another. For this situation, it bodes well that I would just reference the DirectReports settled class with regards to its encasing case of Manager, so I've made it private.
Public nested classes
Because it's
private
, only Manager
can create an instance of DirectReports
. But suppose you wanted to give an external entity the ability to create instances of DirectReports
. In this case, it seems like you could give the DirectReports
class public
scope, and then any external code could create DirectReports
instances, as shown in Listing 19.
Listing 19. Creating DirectReports
instances: First attempt
public class Manager extends Employee { public Manager() { } . . . public class DirectReports { . . . } } // public static void main(String[] args) { Manager.DirectReports dr = new Manager.DirectReports();// This won't work! } |
The code in Listing 19 doesn't work, and you're probably wondering why. The problem (and also its solution) lies with the way
DirectReports
is defined within Manager
, and with the rules of scope.The rules of scope, revisited
If you had a member variable of
Manager
, you'd expect the compiler to require you to have a reference to a Manager
object before you could reference it, right? Well, the same applies to DirectReports
, at least as it's defined in Listing 19.
To create an instance of a public nested class, you use a special version of the
new
operator. Combined with a reference to an enclosing instance of an outer class, new
gives you a way you to create an instance of the nested class:
1
2
3
4
5
6
7
8
9
10
11
12
13
| public class Manager extends Employee { public Manager() { } . . . public class DirectReports { . . . } } // Meanwhile, in another method somewhere... public static void main(String[] args) { Manager manager = new Manager(); Manager.DirectReports dr = manager.new DirectReports(); } |
Note on line 12 that the syntax calls for a reference to the enclosing instance, plus a dot and the
new
keyword, followed by the class you want to create.Static inner classes
At times, you want to create a class that's tightly coupled (conceptually) to a class, but where the rules of scope are somewhat relaxed, not requiring a reference to an enclosing instance. That's where static inner classes come into play. One common example is to implement a
Comparator
, which is used to compare two instances of the same class, usually for the purpose of ordering (or sorting) the classes:public class Manager extends Employee { . . . public static class ManagerComparator implements Comparator< Manager > { . . . } } // Meanwhile, in another method somewhere... public static void main(String[] args) { Manager.ManagerComparator mc = new Manager.ManagerComparator(); . . . } |
For this situation, you needn't bother with an encasing case. Static internal classes act like their general Java class partners, and you ought to utilize them just when you have to couple a class firmly with its definition. Obviously, on account of an utility class like ManagerComparator, making an outer class is superfluous and conceivably jumbles up your code base. Characterizing such classes as static inward classes is the approach.
Anonymous inner classes
With the Java language, you can implement abstract classes and interfaces pretty much anywhere, even in the middle of a method if necessary, and even without providing a name for the class. This capability is basically a compiler trick, but there are times when anonymous inner classes are handy to have.
Listing 20 builds Listing 17, adding a default method for handling
Employee
types that are not StockOptionEligible
. The listing starts with a method in HumanResourcesApplication
to process the stock options, followed by a JUnit test to drive the method.
Listing 20. Handling Employee
types that are not StockOptionEligible
// From HumanResourcesApplication.java public void handleStockOptions(final Person person, StockOptionProcessingCallback callback) { if (person instanceof StockOptionEligible) { // Eligible Person, invoke the callback straight up callback.process((StockOptionEligible)person); } else if (person instanceof Employee) { // Not eligible, but still an Employee. Let's cobble up a /// anonymous inner class implementation for this callback.process(new StockOptionEligible() { @Override public void awardStockOptions(int number, BigDecimal price) { // This employee is not eligible log.warning("It would be nice to award " + number + " of shares at $" + price.setScale(2, RoundingMode.HALF_UP).toPlainString() + ", but unfortunately, Employee " + person.getName() + " is not eligible for Stock Options!"); } }); } else { callback.process(new StockOptionEligible() { @Override public void awardStockOptions(int number, BigDecimal price) { log.severe("Cannot consider awarding " + number + " of shares at $" + price.setScale(2, RoundingMode.HALF_UP).toPlainString() + ", because " + person.getName() + " does not even work here!"); } }); } } // JUnit test to drive it (in HumanResourcesApplicationTest.java): @Test public void testHandleStockOptions() { List< Person > people = HumanResourcesApplication.createPeople(); StockOptionProcessingCallback callback = new StockOptionProcessingCallback() { @Override public void process(StockOptionEligible stockOptionEligible) { BigDecimal reallyCheapPrice = BigDecimal.valueOf(0.01); int numberOfOptions = 10000; stockOptionEligible.awardStockOptions(numberOfOptions, reallyCheapPrice); } }; for (Person person : people) { classUnderTest.handleStockOptions(person, callback); } } |
In the Listing 20 example, I provide implementations of two interfaces that use anonymous inner classes. First are two separate implementations of
StockOptionEligible
— one for Employee
s and one for Person
s (to obey the interface). Then comes an implementation of StockOptionProcessingCallback
that's used to handle processing of stock options for the Manager
instances.
It may require investment to get a handle on the idea of unknown inward classes, however they're super convenient. I utilize them all the time in my Java code. Furthermore, as you advance as a Java designer, I trust you will as well.
0 coment�rios:
Post a Comment