Nested classes

本文详细介绍了Java中嵌套类的概念、定义及使用方法,并通过具体示例展示了匿名内部类在事件处理中的应用。

http://www.ibm.com/developerworks/java/tutorials/j-intermed/section4.html

 

Nested classes


What's a nested class?


As its name suggests, a nested class in the Java language is a class declared within another class. Here's a simple example:

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

Typically, good programmers define nested classes when the nested class only makes sense within the context of the enclosing class. Some common examples include the following:

  • Event handlers within a UI class
  • Helper classes for UI components within those components
  • Adapter classes to convert the innards of one class to some other form for users of the class

You can define a nested class as publicprivate, or protected. You also can define a nested class as final (to prevent it from being changed), abstract (meaning that it can't be instantiated), or static.

 

When you create a static class within another class, you're creating what is appropriately called a nested class. A nested class is defined within another class, but can exist outside an instance of the enclosing class. If your nested class isn't static, it can exist only within an instance of the enclosing class, and is more appropriately called an inner class. In other words, all inner classes are nested classes, but not all nested classes are inner classes. The vast majority of the nested classes you will encounter in your career will be inner classes, rather than simply nested ones.

Any nested class has access to all of the members of the enclosing class, even if they're declared private.

Defining nested classes


You define a nested class just as you define a non-nested class, but you do it within an enclosing class. For a somewhat contrived example, let's define a Wallet class inside Adult. While in real life you could have a Wallet apart from an Adult, it wouldn't be all that useful, and it makes good sense that every Adult has a Wallet (or at least something to hold money, andMoneyContainer sounds a little odd). It also makes sense that Wallet wouldn't exist on Person, because a Baby doesn't have one, and all subclasses of Person would inherit it if it existed up there.

Our Wallet will be quite simple, since it only serves to illustrate the definition of a nested class:

protected class Wallet {
	protected ArrayList bills = new ArrayList();
	
	protected void addBill(int aBill) {
		bills.add(new Integer(aBill));
	}
	
	protected int getMoneyTotal() {
		int total = 0;
		for (Iterator i = bills.iterator(); i.hasNext(); ) {
			Integer wrappedBill = (Integer) i.next(); 
			int bill = wrappedBill.intValue();
			total += bill;
		}
		return total;
	}
}

We'll define that class inside Adult, like this:

public class Adult extends Person {
	protected Wallet wallet = new Wallet();
	public Adult() {
	}
	public void talk() {
		System.out.println("Spoke.");
	}
	public void acceptMoney(int aBill) {
		this.wallet.addBill(aBill);
	}
	public int moneyTotal() {
		return this.wallet.getMoneyTotal();
	}
	protected class Wallet {
		...
	}
}

Notice that we added acceptMoney() to let an Adult accept more money. (Feel free to expand the example to force your Adult to give some up, which is a more common event in real life.)

Once we have our nested class and our new acceptMoney() method, we can use them like this:

Adult anAdult = new Adult();
anAdult.acceptMoney(5);
System.out.println("I have this much money: " + anAdult.moneyTotal());

Executing this code should report that anAdult has a money total of 5.

 

Simplistic event handling

 

The Java language defines an event handling approach, with associated classes, that allows you to create and handle your own events. But event handling can be much simpler than that. All you really need is some logic to generate an "event" (which really doesn't have to be an event class at all), and some logic to listen for that event and then respond appropriately. For example, suppose that whenever a Person moves, our system generates (or fires) a MoveEvent, which we can choose to handle or not. This will require several changes to our system. We have to:

  • Create an "application" class to launch our system and illustrate using the anonymous inner class.
  • Create a MotionListener that our application can implement, and then handle the event in the listener.
  • Add a List of listeners to Adult.
  • Add an addMotionListener() method to Adult to register a listener.
  • Add a fireMoveEvent() method to Adult so that it can tell listeners when to handle the event.
  • Add code to our application to create an Adult and register itself as a handler.

This is all straightforward. Here's our Adult with the new stuff:

public class Adult extends Person {
	protected Wallet wallet = new Wallet();
	protected ArrayList listeners = new ArrayList();
	public Adult() {
	}
	public void move() {
	  super.move(); fireMoveEvent();
	}
	...
	public void addMotionListener(MotionListener aListener) {
	  listeners.add(aListener);
	}
	protected void fireMoveEvent() {
	  Iterator iterator = listeners.iterator();
	  while(iterator.hasNext()) {
	    MotionListener listener = (MotionListener) iterator.next();
	    listener.handleMove(this);
	}
	}
	protected class Wallet {
		...
	}
}

Notice that we now override move(), call move() on Person first, then call fireMoveEvent() to tell listeners to respond. We also added addMotionListener() to add a MotionListener to a running list of listeners. Here's what a MotionListener looks like:

public interface MotionListener {
	public void handleMove(Adult eventSource);
}

All that's left is to create our application class:

public class CommunityApplication implements MotionListener {
	public void handleMove(Adult eventSource) {
		System.out.println("This Adult moved: /n" + eventSource.toString());
	}
	public static void main(String[] args) {
		CommunityApplication application = new CommunityApplication();
		Adult anAdult = new Adult();
		anAdult.addMotionListener(application);
		anAdult.move();
	}
}

This class implements the MotionListener interface, which means that it implements handleMove(). All we do here is print a message to illustrate what happens when an event is fired.

Anonymous inner classes


Anonymous inner classes allow you to define a class in place, without naming it, to provide some context-specific behavior. It's a common approach for event handlers in user interfaces, which is a topic beyond the scope of this tutorial. But we can use an anonymous inner class even in our simplistic event-handling example.

You can convert the example from the previous page to use an anonymous inner class by changing the call toaddMotionListener() in CommunityApplication.main() like so:

anAdult.addMotionListener(new MotionListener() {
  public void handleMove(Adult eventSource) {
    System.out.println("This Adult moved: /n" + eventSource.toString());
  }
});

Rather than having CommunityApplication implement MotionListener, we declared an unnamed (and thus anonymous) inner class of type MotionListener, and gave it an implementation of handleMove(). The fact that MotionListener is an interface, not a class, doesn't matter. Either is acceptable.

This code produces exactly the same result as the previous version, but it uses a more common and expected approach. You will almost always see event handlers implemented with anonymous inner classes.

Using nested classes


Nested classes can be very useful. They also can cause pain.

Use a nested class when it would make little sense to define the class outside of an enclosing class. In our example, we could have defined Wallet outside Adult without feeling too badly about it. But imagine something like a Personality class. Do you ever have one outside a Person instance? No, so it makes perfect sense to define it as a nested class. A good rule of thumb is that you should define a class as non-nested until it's obvious that it should be nested, then refactor to nest it.


Anonymous inner classes are the standard approach for event handlers, so use them for that purpose. In other cases, be very careful with them. Unless anonymous inner classes are small, focused, and familiar, they obfuscate code. They can also make debugging more difficult, although the Eclipse IDE helps minimize that pain. Generally, try not to use anonymous inner classes for anything but event handlers.

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值