Introduction to Java programming, Part 2: Constructs for real-world applications

本文详细介绍了Java中嵌套类的使用场景与规则,包括其作用域、静态内部类及匿名内部类等概念,并通过具体实例说明如何在实际编程中应用。

http://www.ibm.com/developerworks/java/tutorials/j-introtojava2/section7.html

 

Nested classes

In this section, learn about nested classes and where and how to use them.

Where to use nested classes

As its name suggests, a nested class is one defined within another class. Here is a nested class:

public class EnclosingClass {
. . .
 public class NestedClass {
 . . .

 }
}

Just like member variables and methods, Java classes can also be defined at any scope including publicprivate, orprotected. Nested classes can be useful when you want to handle internal processing within your class in an object-oriented fashion, but this functionality is limited to the class where you need it.

Typically, you'll use a nested class for cases where you need a class that is tightly coupled with the class in which it is defined. A nested class has access to the private data within its enclosing class, but this carries with it some side-effects that are not obvious when you start working with nested (or inner) classes.

Scope in nested classes

Because a nested class has scope, it is 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 {
 . . .
 }
}

Just as each Manager object represents a unique human being, the DirectReports object represents a collection of actual people (employees) who report to a manager. DirectReports will differ from one Manager to another. In this case, it makes sense that one would only reference the DirectReports nested class in the context of its enclosing instance of Manager, so I've made itprivate.

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 17:


Listing 17. Creating DirectReports instances: First attempt
public class Manager extends Employee {
 public Manager() {
 }
. . .
 private class DirectReports {
 . . .
 }
}
//
public static void main(String[] args) {
 Manager.DirectReports dr = new Manager.DirectReports();// This won't work!
}

The code in Listing 17 doesn't work, and you're probably wondering why. The problem (and also its solution) lies with the wayDirectReports is defined within Manager, and with the rules of scope.

The rules of scope, revisited

If you had a member variable of Manager, you would 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 you defined it in Listing 17.

To create an instance of a public nested class, you use a special version of the new operator. Combined with a reference to some enclosing instance of an outer class, new allows you to create an instance of the nested class:

public class Manager extends Employee {
 public Manager() {
 }
. . .
 private class DirectReports {
 . . .
 }
}
// Meanwhile, in another method somewhere...
public static void main(String[] args) {
 Manager manager = new Manager();
 Manager.DirectReports dr = manager.new DirectReports();
}

Note 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 will want to create a class that is 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 of this 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();
 . . .
}

In this case, you don't need an enclosing instance. Static inner classes act like their regular Java class counterparts, and they should really only be used when you need to couple a class tightly with its definition. Clearly, in the case of a utility class likeManagerComparator, creating an external class is unnecessary and potentially clutters up your code base. Defining such classes as static inner classes is the way to go.

Anonymous inner classes

The Java language allows you to declare classes pretty much anywhere, even in the middle of a method if necessary, and even without providing a name for the class. This is basically a compiler trick, but there are times when anonymous inner classes are extremely handy to have.

Listing 18 builds on the example in Listing 15, adding a default method for handling Employee types that are notStockOptionEligible:


Listing 18. Handling Employee types that are not StockOptionEligible
public static void main(String[] args) {
 Employee employee = new Manager();// perfectly valid
 handleStockOptions(employee);
 employee = new Employee();// not StockOptionEligible
 handleStockOptions(employee);
}
. . .
private static void handleStockOptions(Employee e) {
 if (e instanceof StockOptionEligible) {
   calculateAndAwardStockOptions((StockOptionEligible)e);
 } else {
   calculateAndAwardStockOptions(new StockOptionEligible() {
     @Override
     public void awardStockOptions(int number, BigDecimal price) {
       log.info("Sorry, you're not StockOptionEligible!");
     }
   });
 }
}
. . .
private static void calculateAndAwardStockOptions(StockOptionEligible soe) {
 BigDecimal reallyCheapPrice = BigDecimal.valueOf(0.01);
 int numberOfOptions = 10000;
 soe.processStockOptions(numberOfOptions, reallyCheapPrice);

}

In this example, you provide an implementation of the StockOptionEligible interface by using an anonymous inner class for instances of Employee that do not implement that interface. Anonymous inner classes are also useful for implementing callback methods, and in event handling, too.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值