So what are inner classes good for anyway?(翻译 by Gee)

来源:互联网 发布:粒子群算法参数 编辑:程序博客网 时间:2024/05/01 05:07

Q:其实内部类有什么好处呢?

A:不管怎么说,内部类的存在有着它的长处。在我们开始探讨之前,让我来简单地介绍一下内部类的来由。

      内部类就是嵌套在另一个类的里面的类。一个一般的类是package的直接成员,也就是最顶层的类,而内部类就是在jdk1.1就出现的概念,它主要分成四类:

1 静态内部类

2  一般内部类

3  局部类

4  匿名类

让我按次序来简单认识一下吧

简短地说,静态内部类也就是一个一般类的一个静态成员。像其他的静态方法一样,静态内部类可以访问顶层类的所有静态方法。

像静态内部类一样,一般内部类也是扮演着一般类的一个内部成员。但它不像静态内部类,一般内部类需要声明实例化,可以访问所有的成员和方法,甚至可以通过访问this这个reference访问顶层类的成员和方法。

局部类就是在一些语句块内声明和只能在语句块内才可以看到的类,就好像方法内的局部变量一样。

最后是匿名类,他也就是一个没有名字的局部类。

为了更明确的解答你的问题,我会把着重点放到你将会遇到的或者用上的一般内部类和匿名类。从我的角度来说,内部类的好处可以分成三个类别:在面向对象上的好处、在组织结构上好处和回调的好处。

在面向对象上的好处

以我的浅陋的认识来看,内部类最重要的特点就是允许我们把一些东西放到对象里面去,因为正常情况下我们一般不会这样做的。与如果没有内部类的出现相比,内部类这样的特点让我们的代码更加符合面向对象的思想。

让我们来看一下一般内部类,自从它的实例对象成为父实例对象的一个成员部分后,它可以访问父实例对象的所有成员属性和方法。乍眼看去,这好像不大像;但实际上我们已经访问了父实例对象内的方法了。然而内部类又允许我们在逻辑上从父实例对象中分离出来并具体化它。举个例子,一个树类,可能有一个方法或者许多辅助方法来实现这棵树的搜索或者遍历。从面对对象的观点来看,树类就是一棵树,不是一个搜索算法。但你要完成一个树的搜索操作,就必须和这棵树的数据结构有一个密切的关系。

一个内部类就是可以把这些逻辑去掉并把它们封装成一个自己的类。所以从面向对象的观点来说,我们已经把那些不属于该类的功能性的东西封装成一个自己的类。通过应用内部类,我们成功地把其搜索算法从树类中分离出来。那么现在,假如我们要改变搜索算法,我们就简单地替换这个内部类就可以了。而内部类在面向对象上的好处就说到这里了,其实我可以继续说下去,但这样会不断扩展地展示代码中面向对象技术带来的好处。

组织结构上的好处

面向对象设计并不是适合所有的东西的,但幸运的是,内部类还提供了其他更多的好处。从组织结构上的观点来看,内部类允许我们通过运用命名空间来更深层次地组织我们的包结构。类与类之间可以更深层次的嵌套,让层次结构更清晰,取代了这些类一箩筐似的倒在同一层次的包里面。显而易见,如果没有内部类,我们会局限于下面的层次结构:

package1
   class 1
      class 2
      ...
      class n
...
package n

但如果有内部类,我们就可以实现一下这样:

package 1
   class 1
   class 2
      class 1
      class 2
      ...
      class n

只要我们能够适当地运用,内部类会让我们的类具有更自然的层次结构。

 

回调上的好处

一般内部类和匿名类提供给我们一个方便的方法来定义回调。最典型的例子就是GUI代码上的应用。但是,应用程序上的回调可以扩展到很多域中去。

大部分的Java GUIs都有一些组件响应actionPerformed()方法调用。不幸地是,大部分的开发人员只是简单地在他们的主窗口的程序上实现ActionListener。结果所有的组件都共享这个actionPerformed()方法。要想指出哪个组件去实现动作,actionPerformed()方法不仅篇幅大,而且代码中还要存在难读的switch语句。

这里有个关于单片电路实现的例子:

public class SomeGUI extends JFrame implements ActionListener
{
   protected JButton button1;
   protected JButton button2;
   ...
   protected JButton buttonN;

   public void actionPerformed(ActionEvent e)
   {
      if(e.getSource()==button1)
      {
         // do something
         }
      else if(e.getSource()==button2)
      {
         ... you get the picture

无论什么时候,当你看到多个switch或者大篇幅的if/if else的代码块,你就应该在脑袋中敲起警钟。总的来说,这些结构都是不好的面向对象的设计,一旦代码中一个部分改变了就要在switch条件中做出相应的修改。而一般内部类和匿名类就可以让我们摆脱多个switch条件的actionPerformed()方法。

事实上,我们可以为需要监听的每个组件定义一个内部类去实现ActionListener,当然这样会导致很多个内部类,但我们可以避免了多个switch条件语句和拥有了多个独立封装action逻辑带来的灵活性。此外,这样做也提高了程序性能。比如说多个switch条件语句中有n次比较,我们估算大概平均要n/2次地比较次数。而内部类可以让我们在action performeraction listener之间达到11的匹配对应。在大型的GUI程序中,这样的优化工作对性能上的改进会产生一个实质性的影响。一个匿名类实现这种优化写法大概如下:

public class SomeGUI extends JFrame
{
   ... button member declarations ...

   protected void buildGUI()
   {
      button1 = new JButton();
      button2 = new JButton();
      ...

      button1.addActionListener(
         new java.awt.event.ActionListener()
         {
            public void actionPerformed(java.awt.event.ActionEvent e)
            {
               // do something
            }
         }
      );
      …repeat for each button

 

 如果应用一般内部类,程序就会像这样:

public class SomeGUI extends JFrame
{
   ... button member declarations

   // inner class definitions
   class Button1Handler implements ActionListener
   {
      public void actionPerformed(ActionEvent e)
   {
         // do something
      }
   }
   ... define an inner member class for each button

   protected void buildGUI()
   {
      // initialize the buttons
      button1 = new JButton();
      button2 = new JButton();
      ...

      // register an inner class action listener instance
      // for each button
      button1.addActionListener(new Button1Handler());
      .. repeat for each button

因为内部类可以访问父类的所有东西,所以我们可以把单片电路程序中任意一个actionPerformed()方法的实现放到一个内部类中去,以这种方式来移动程序的逻辑块。

我个人比较偏向于应用一般内部类来实现回调,但这只是个人的喜好。因为我觉得太多匿名类会混乱了代码,同时如果匿名类超过两句代码,我就会觉得它比较笨拙。

内部类不足的地方

没有东西是十全十美的,一样东西有它的长处必然会有它的短处。内部类也有它们不足的地方。从维护的角度来说,一个不熟练的开发人员就会觉得内部类难以理解。应用内部类会使增加你的类的代码总行数。此外,从开发的角度来说,大部分的Java工具都在对内部类的支持上都存在或多或少不足的地方。

For example, I use IBM's VisualAge for Java for my day-to-day coding. While inner classes will compile within VisualAge, there is no inner class browser or template. Instead, you must simply type the inner class directly into the class definition. That unfortunately makes browsing the inner class difficult. It is also difficult to type since you lose many of VisualAge's code completion aids when you type into the class definition or use an inner class.

原文链接:

http://www.javaworld.com/javaworld/javaqa/2000-03/02-qa-innerclass.html

 

 

原创粉丝点击