`
DBear
  • 浏览: 229051 次
  • 性别: Icon_minigender_2
  • 来自: 上海
社区版块
存档分类

Inner Classes小结

阅读更多

   Inner Classes 在平常的应用中虽然不是很多,但是仍然有很多细节问题值得注意,需要掌握。因此,这里对这方面内容做一小结。

   Inner Classes,顾名思义,就是定义在另一个类中的类,之所以出现这一概念,主要是因为它具有如下两个特点:  

  1. inner classes中的方法可以访问类定义域内的所有数据, 哪怕数据是private的。
  2. inner classes对同包内的其它类是不可见的。

   常规内部类

   下面,我将针对一段具体代码,对Inner Classes的特性和应用作具体的阐述。

public class TimerTestForInnerClass {
    public static void main(String[] args) {
        TalkingClock clock = new TalkingClock(100, true);
        clock.start();
	
        JOptionPane.showMessageDialog(null, "Quit Program?");
        System.exit(0);
    }
}

class TalkingClock {
  private int interval;
  private boolean beep;
  
  public TalkingClock(int interval, boolean beep) {
      this.interval = interval;
      this.beep = beep;
  }
  
  public void start() {
      ActionListener listener = new TimePrinter();
      Timer t = new Timer(interval, listener);
      t.start();
  }
  
  public class TimePrinter implements ActionListener {
      public void actionPerformed(ActionEvent event) {
          Date now = new Date();
          System.out.println("At the tone, the time is " + now);
          if (beep) Toolkit.getDefaultToolkit().beep();
      }
  }
  
}

 

    在上面的代码中,TimePrinter是TalkingClock的内部类。仔细观察,发现actionPerformed方法中引用的beep变量在TimePrinter中并没有定义,它是外部类TalkingClock的成员变量。这就是inner classes特点一的展示,之所以可以如此,是因为inner class在被创建时获得了外部类的一个引用,这个引用是由编译器自动传给内部类的构造函数的,因此,内部类中使用的beep变量的完整形式其实是:

 

if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();

   上述代码中的OuterClass.this代表的就是外部类的引用。

   如果内部类是public的,那么我们也可以在外部类以外来创建内部类,方法如下:

TalkingClock jabberer = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();

  

Local Inner Classes 局部内部类

   内部类除了可以作为外部类的成员,还可以定义在外部类的方法中,这样的内部类称为:Local Inner Classes。Local Inner Classes不需要访问权限修饰符(access specifier),因为它们的作用域受它们被定义的块域的限制。Local Inner Classes的一个重要的优点是,除了定义它们的方法以外,外界任何类或方法都无法访问它们。因此,当一个内部类只需要被一个方法使用,并且想屏蔽任何其他方法时,就可以将它们定义为Local的。

public void start() {
    class TimePrinter implements ActionListener {
      public void actionPerformed(ActionEvent event) {
          Date now = new Date();
          System.out.println("At the tone, the time is " + now);
          if (beep) Toolkit.getDefaultToolkit().beep();
      }
    }
    ActionListener listener = new TimePrinter();
    Timer t = new Timer(interval, listener);
    t.start();
}

   上面的代码就是Local Inner Class的应用实例。

   

    Local Inner Class不光可以使用Outer Class的成员变量,还可使用它所在方法的参数以及方法中的局部变量。但要注意一点,这些外部引用变量都必须是final型,否则会收到编译器错误。见下面的代码:

public void start(int interval, final boolean beep) {
    class TimePrinter implements ActionListener {
      public void actionPerformed(ActionEvent event) {
          Date now = new Date();
          System.out.println("At the tone, the time is " + now);
          if (beep) Toolkit.getDefaultToolkit().beep();
      }
    }
    ActionListener listener = new TimePrinter();
    Timer t = new Timer(interval, listener);
    t.start();
}

   我们来简单了解下beep参数是如何传入内部类的方法中。直观来看,beep变量是无法存留到actionPerformed方法执行之时的,它在start()方法执行结束之后就被回收了,而actionPerformed则要过短时间才执行,因此,为了正常使用beep变量,我们需要为它做一个copy,并将这个copy传进内部类中。所以,内部类中所谓的beep,其实是原有变量的副本。

 

TIP:为什么只有final型的参数或局部变量可以传入inner class?(整理自Core Java,如有不妥,敬请指正)

在很多时候,这个final的限制其实为我们带来了很多不便,因为我们有的时候确实是想要在inner class中改变局部变量的,如下面这段代码:

int counter = 0;
Date[] dates = new Date[100];
for (int i = 0; i < dates.length; i++)
   dates[i] = new Date() {
          public int compareTo(Date other) {
              counter++; // ERROR

              return super.compareTo(other);
          }
   };

Arrays.sort(dates);
System.out.println(counter + " comparisons.");

 

   我们只是想获取比较次数,但是由于内部类的final限制,这样的代码显然是不合法的。但如果直接加上final,我们的update目标又无法达到,所以,只能做如下的转换:

final int[] counter = new int[1];
for (int i = 0; i < dates.length; i++)
   dates[i] = new Date() {
          public int compareTo(Date other) {
              counter[0]++;
              return super.compareTo(other);
          }
   };

    这样,虽然counter是final的,但是我们却可以随意改变它内部的值,从而变相的达到了update的目标。

    事实上,在inner class起初创建时,这种从primitive型到array型的转换最开始是由编译器自动完成的,但是开发者们很害怕编译器在“背着他们”创建那么些对象,因此,最后还是决定加上final这个限制,如果需要在inner class中改变变量值,那就让programer自己去做转换。而且,也并不排除,随着Java语言的不断完善,会有更好的方式来替代final限制,达到原有的安全效果。

  

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics