Sunday, May 28, 2017

Introduction to Java programming, Part 2 (7)

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 Employees 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 DirectReportsinstances, 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 newkeyword, 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 Employees and one for Persons (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.
Previous Post
Next Post

post written by:

0 coment�rios: