第10章 内部类

10.1 创建内部类

此标题如若修改为:定义内部类创建,我觉得更合适一些。

定义内部类的方法:把类的定义置于外围类的里面

创建内部类的方法:

OuterClassName.InnerClassName innerClassInstance = outerClassInstance.new InnerClassName(Para list);

Sample:

 1 class OuterClass {
 2     public class Inner1 {
 3         public Inner1(String name) {
 4             this.name = name;
 5         }
 6         private String name;
 7     }
 8                                                             
 9     protected class Inner2 {
10     }
11                                                             
12     class Inner3 {
13     }
14                                                             
15     private class Inner4 {
16     }
17 }
18                                                             
19 public class Test {
20     public static void main(String[] args) {
21         OuterClass outer = new OuterClass();
22         OuterClass.Inner1 inner1 = outer.new Inner1("Inner1");
23         OuterClass.Inner2 inner2 = outer.new Inner2();
24         OuterClass.Inner3 inner3 = outer.new Inner3();
25         // Of course you can NOT create a inner class not accessible.
26         //OuterClass.Inner4 inner4 = outer.new Inner4();
27     }
28 }

10.2 链接到外部类

Java内部类的最显著的特点,这也是与C++的嵌套类的最显著的不同 -- 用书上的原话来说:”当生成一个内部类的对象时,此对象与制造它的外围对象(enclosing object)之间就有了一种联系,所以它能访问外围对象的所有成员,而不需要任何特殊条件。“。内部类自动拥有对其外围类所有从成员的访问权限,包括private。

书上用了一个Iterator模式的例子。

 1 interface Selector {
 2     boolean end();
 3     Object current();
 4     void next();
 5 }
 6               
 7 public class Sequence {
 8     private Object[] items;
 9     private int next = 0;
10     public Sequence(int size) { items = new Object[size];}
11     public void add(Object x) {
12         if (next < items.length)
13             items[next++] = x;
14     }
15                   
16     private class SequenceSelector implements Selector {
17         private int i = 0;
18         public boolean end() { return i == items.length; }
19         public Object current() { return items[i]; }
20         public void next() { if(i < items.length) i++; }
21     }
22     public Selector selector() { return new SequenceSelector(); }
23                   
24     public static void main(String[] args) {
25         Sequence sequence = new Sequence(10);
26         for(int i = 0; i < 10; ++i)
27             sequence.add(Integer.toString(i));
28         Selector selector = sequence.selector();
29         while(!selector.end()){
30             System.out.println(selector.current() + "");
31             selector.next();
32         }
33     }
34 }

10.3 使用.this与.new

在Inner类内部如何使用外部类的对象引用呢?OuterClassName.this,也就是.this用法。目前有点无法理解.this的用途,因为对于内部类来说,它总是可以无条件的直接访问外部类的所有成员(参见上一节);对于使用内部类的客户端来说,在创建内部类的时候因为必须用外部类的对象+.new的方式生成一个内部类的instance,所以客户端已经有了外部类对象的句柄了。

答案来了,代码来自http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

 1 public class ShadowTest {
 2 
 3     public int x = 0;
 4 
 5     class FirstLevel {
 6 
 7         public int x = 1;
 8 
 9         void methodInFirstLevel(int x) {
10             System.out.println("x = " + x);
11             System.out.println("this.x = " + this.x);
12             System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
13         }
14     }
15 
16     public static void main(String... args) {
17         ShadowTest st = new ShadowTest();
18         ShadowTest.FirstLevel fl = st.new FirstLevel();
19         fl.methodInFirstLevel(23);
20     }
21 }/*
22 x = 23
23 this.x = 1
24 ShadowTest.this.x = 0
25 */

10.4 内部类与向上转型

定义一个外部public interface(或者是抽象类,或者是某个基类),在某个类内部定义一个内部类实现该接口(抽象类,或者基类的方法),通过设置该内部类的访问控制权限(比如设置为私有,包权限,或者protected权限),使该接口(抽象类,或者基类)的实现对外不可见。从而达到了隐藏细节的目的。

例子可以参见10.2的代码。

10.5 在方法和作用域内的内部类

也就是在C++中所说的Local Class,既然说到了C++,就先来回忆一下的Local Class。

C++中Local Class的定义:一个定义与函数(方法)中的Class。它有如下的使用规则:

  1. 声明于Local Class定义所在Function之前的全局变量可通过域作用符(::)访问。
  2. Local Class定义所在Function之中声明的static变量也可被访问。
  3. Local Class定义所在Function之中声明的局部变量不可访问。
  4. Local Class不可以有static data member。
  5. Local Class的所有member function必须被定义在Class内部。
  6. Local Class定义所在的Function(有一个专业术语叫enclosing function)不可以访问Local Class中定义的私有成员。

来看一段C++的代码:

 1 #include <iostream>  
 2 int y;  
 3 void g();  
 4 int main()  
 5 {  
 6     g();  
 7     return 0;  
 8 }  
 9 
10 void g()                        
11 {
12     static int x = 4;
13     class local{  
14     public:  
15         void put( int n) {
16             ::y=n;
17             y = x; //You could not refer y by using this.y which will result an compile error.
18         }
19         int getGlobalY() {return ::y;}
20         int getLocalY() {return y;}
21     private:
22         int y;
23     } ab;  
24     ab.put(20);  
25     ::std::cout << "The value assigned to global y is::" << ab.getGlobalY() << ::std::endl;
26     ::std::cout << "The value assigned to local y is::" << ab.getLocalY() << ::std::endl;
27     // you can NOT access private member of local class
28     //::std::cout << "The value assigned to local y is::" << ab.y;
29 }

 10.6 匿名内部类

一个典型的匿名内部类的用法(去实现一个接口)

 1 import static java.lang.System.out;
 2 interface IActionListener {
 3   void clicked();
 4 }
 5 
 6 public class Button{
 7     public IActionListener getActionListener(){
 8       return new IActionListener() {
 9         public void clicked(){
10           out.println("Button(" + name + ").clicked() in anonymouse inner class");
11         }
12       };
13     }
14     Button(String name){
15       this.name = name;
16     }
17       private String name = "";
18     public static void main(String args[]){
19         new Button("Open File").getActionListener().clicked();
20     }
21 }/*
22 clicked() in anonymouse inner class
23 */

 

一个稍微迷惑人的用法是去继承另外一个类(而不是一个接口),匿名内部类因为没有名字,所以无法定义它的构造函数,但我们可以把传递给Base类参数。另外可以匿名内部类可以有自己的初始化块(但内部类不可以有静态域)。另外内部类的某个方法如果需要传递参数,那么参数必须声明为final的。

 1 import static java.lang.System.out;
 2 class ActionListener {
 3   ActionListener(String name){
 4     System.out.println("ActionListern constructor of Widget " + name);
 5     widgetName = name;
 6   }
 7   void clicked(final String surfix){};
 8   private String widgetName = "";
 9 }
10 
11 public class Button{
12     public ActionListener getActionListener(){
13       return new ActionListener("Button") {
14         /*error: Illegal static declaration in inner class
15         static private String surfix = "1";
16         static {
17           surfix = "1111";
18         }
19         */
20         private String prefix = "Button";
21         {
22           prefix = "Button1";
23         }
24         public void clicked(final String surfix){
25           out.println("Button(" + name + ").clicked() in anonymouse inner class");
26           out.println(prefix + surfix);
27         }
28         class Test{ /*Of course, inner class can defined in local class*/
29         }
30       };
31     }
32     Button(String name){
33       this.name = name;
34     }
35       private String name = "";
36     public static void main(String args[]){
37         new Button("Open File").getActionListener().clicked(".btn");
38     }
39 }/*
40 ActionListern constructor of Widget Button
41 Button(Open File).clicked() in anonymouse inner class
42 Button1.btn
43 */

10.7 嵌套类

前面所设计到的内部类看上去与C++的InnerClass有很大的不同,最大的区别在于,C++的InnerClass更像一种Namespace的嵌套,InnerClass与Enclosing Class的实例之间没有任何关系,但Java的内部类因为由Enclosing Class的Object.new创建,自然拥有了对Enclosing Object的一切访问权限。

那么在Java中有没有与C++的InnerClass同等概念的InnerClass呢?有,那就是定义为static的嵌套类。

接口的内部类自动是public static;多层嵌套内部类访问外部类的成员是透明的。

10.8 为什么需要内部类

多了一种实现多重继承的选择。比如有两个Interface A和B,要在同一个类中实现这两个Interface,既可以选择多重继承,也可以选择单继承+内部类的方式,下面就来看一看这两种方式的实现方法。

 1 import static java.lang.System.out;
 2 interface A {}
 3 interface B {
 4     void f();
 5 }
 6 
 7 class X implements A, B {
 8     public void f() {out.println("B.f() in X");}
 9 }
10 
11 class Y implements A {
12     B getB() {
13         return new B() {
14             public void f() {out.println("B.f() in Y");}
15         };
16     }
17 }
18 
19 public class Test {
20     static void takesA(A a){}
21     static void takesB(B b){b.f();}
22     public static void main(String args[]) {
23         X x = new X();
24         Y y = new Y();
25         takesA(x);
26         takesA(y);
27         takesB(x);
28         takesB(y.getB());
29     }
30 }/* Output:
31 B.f() in X
32 B.f() in Y
33 */

显然class Y的方式具有更大的灵活性,原因有若干个:

  1. class Y不一定非得和interface B保持一种is-a的关系。
  2. 如果上面的例子中A与B不是interface而都是abstract class或者都是class那么用class X的方式就做不到了,而使用内部类的class Y的方式则不受限制。
  3. 与上一条中的原因相同,如果需要实现的两个接口中含有同样签名的方法,那么用简单的继承(implements)的方式显然无法办到,这时候用内部类是一个比较好的解决方法。
  4. 使用内部类可以实现同一接口在同一个对象上展现不同的行为,关于这一点我们来看一下如下的代码片段。
 1 import static java.lang.System.out;
 2 interface B {
 3     void f();
 4 }
 5 class Y {
 6     B getB1() {
 7         return new B() {
 8             public void f() {out.println("B1.f() in Y");}
 9         };
10     }
11     B getB2() {
12         return new B() {
13             public void f() {out.println("B2.f() in Y");}
14         };
15     }
16 }
17 public class Test {
18     static void takesB(B b){b.f();}
19     public static void main(String args[]) {
20         Y y = new Y();
21         takesB(y.getB1());
22         takesB(y.getB2());
23     }
24 }/* Output:
25 B1.f() in Y
26 B2.f() in Y
27 */

 

一个Class Y就可以实现对同一个Interface B的f()方法产生两种完全不同的行为,这为多态增添了更多的意义。

posted on 2013-05-02 16:00  peter9606  阅读(170)  评论(0编辑  收藏  举报

导航