深入了解JAVA内部类

将一个类的定义放在另一个类的定义内部,这就是内部类。它允许你把一些逻辑相关的类组织在一起,并控制内部类的可视性—— thinking in java 4

本文将介绍:内部类的经典个性(与众不同),试图解释为什么需要引入内部类这个很烦的概念,以及什么场景需要运用内部类(这也是困扰笔者很久的问题).

(您可能需要先了解内部类的基本语法:匿名内部类,嵌套内部类,内部类的多种写法)

1.自由引用外部类的属性(即使外部类的属性定义为private)。

interface Selector {
  boolean end();
  Object current();
  void next();
}	

public class Sequence {
  private Object[] items;
  private int next = 0;
  public Sequence(int size) { items = new Object[size]; }
  public void add(Object x) {
    if(next < items.length)
      items[next++] = x;
  }
  private class SequenceSelector implements Selector {
    private int i = 0;
    public boolean end() { return i == items.length; }
    public Object current() { return items[i]; }
    public void next() { if(i < items.length) i++; }
  }
  public Selector selector() {
    return new SequenceSelector();
  }	
  public static void main(String[] args) {
    Sequence sequence = new Sequence(10);
    for(int i = 0; i < 10; i++)
      sequence.add(Integer.toString(i));
    Selector selector = sequence.selector();
    while(!selector.end()) {
      System.out.print(selector.current() + " ");
      selector.next();
    }
  }
} /* Output:
0 1 2 3 4 5 6 7 8 9
*///:~
Sequence类的作用是动态创建数组并赋值,它的内部类SequenceSelector实现了Selector接口,重写了其中的方法。

这里的items是定义为private的外部类的一个属性,却可以被内部类sequenceSelector自由引用,这样就带来了很大的灵活性!

2.对外完全隐藏具体实现

class Parcel4 {
  private class PContents implements Contents {
    private int i = 11;
    public int value() { return i; }
  }
  protected class PDestination implements Destination {
    private String label;
    private PDestination(String whereTo) {
      label = whereTo;
    }
    public String readLabel() { return label; }
  }
  public Destination destination(String s) {
    return new PDestination(s);
  }
  public Contents contents() {
    return new PContents();
  }
}

public class TestParcel {
  public static void main(String[] args) {
    Parcel4 p = new Parcel4();
    Contents c = p.contents();
    Destination d = p.destination("Tasmania");
    // Illegal -- can't access private class:
    //! Parcel4.PContents pc = p.new PContents();
  }
} ///:~
对于内部类PContents,因为是定义为private的,除了类Parcel4没人能访问它,对于内部类PDestination,定义为protected,只有Parcel4和它的子类以及同包的类可以访问,对外完全隐藏了实现细节!

3.自由灵活简洁的编码风格

interface Service {
  void method1();
  void method2();
}

interface ServiceFactory {
  Service getService();
}	

class Implementation1 implements Service {
  private Implementation1() {}
  public void method1() {print("Implementation1 method1");}
  public void method2() {print("Implementation1 method2");}
  public static ServiceFactory factory =
    new ServiceFactory() {
      public Service getService() {
        return new Implementation1();
      }
    };
}	

class Implementation2 implements Service {
  private Implementation2() {}
  public void method1() {print("Implementation2 method1");}
  public void method2() {print("Implementation2 method2");}
  public static ServiceFactory factory =
    new ServiceFactory() {
      public Service getService() {
        return new Implementation2();
      }
    };
}	

public class Factories {
  public static void serviceConsumer(ServiceFactory fact) {
    Service s = fact.getService();
    s.method1();
    s.method2();
  }
  public static void main(String[] args) {
    serviceConsumer(Implementation1.factory);
    // Implementations are completely interchangeable:
    serviceConsumer(Implementation2.factory);
  }
} /* Output:
Implementation1 method1
Implementation1 method2
Implementation2 method1
Implementation2 method2
*///:~
这是一个简单的工厂模式,按照常规的写法这里需要四个实现类,分别实现ServiceFactory和Service接口,使用匿名内部类,甚至连工厂名都不需要了,有没有感觉代码瞬间简介了许多,呵呵!
我们甚至可以在接口中定义自己的内部类,这样实现了这个接口的所有类都能共用这段代码(似乎又变回了abstract  class 但是又有interface的功能,因为你可以implements 多个interface,嗯,很好玩!)

public interface ClassInInterface {
  void howdy();
  class Test implements ClassInInterface {
    public void howdy() {
      System.out.println("Howdy!");
    }
    public static void main(String[] args) {
      new Test().howdy();
    }
  }
} /* Output:
Howdy!
*///:~

OK,关键问题来啦,为什么SUN公司要费尽心机引入内部类,我们什么时候需要它呢?

官方回答是这样:一般来说,内部类继承某个类或实现某个接口,同时可以引用外部类的属性,是外部类的“窗口”,我们知道JAVA是没有多继承的,引入内部类就是为了解决这个问题!来看例子吧。

我们假设某种应用场景

interface A {}
interface B {}

class X implements A, B {}

class Y implements A {
  B makeB() {
    // Anonymous inner class:
    return new B() {};
  }
}

public class MultiInterfaces {
  static void takesA(A a) {}
  static void takesB(B b) {}
  public static void main(String[] args) {
    X x = new X();
    Y y = new Y();
    takesA(x);
    takesA(y);
    takesB(x);
    takesB(y.makeB());
  }
} ///:~

如果是实现两个接口,这里的两种方式都是可以的,但是如果应用场景必须要你继承多个抽象或具体的类呢?

class D {}
abstract class E {}

class Z extends D {
  E makeE() { return new E() {}; }
}

public class MultiImplementation {
  static void takesD(D d) {}
  static void takesE(E e) {}
  public static void main(String[] args) {
    Z z = new Z();
    takesD(z);
    takesE(z.makeE());
  }
} ///:~

我们可以避开多重继承,用内部类来解决问题——需要继承多少类就可以有多少个内部类,且不受外部类的影响!
再来看一个经典的应用场景:

interface Incrementable {
  void increment();
}

// Very simple to just implement the interface:
class Callee1 implements Incrementable {
  private int i = 0;
  public void increment() {
    i++;
    print(i);
  }
}	

class MyIncrement {
  public void increment() { print("Other operation"); }
  static void f(MyIncrement mi) { mi.increment(); }
}	

// If your class must implement increment() in
// some other way, you must use an inner class:
class Callee2 extends MyIncrement {
  private int i = 0;
  public void increment() {
    super.increment();
    i++;
    print(i);
  }
  private class Closure implements Incrementable {
    public void increment() {
      // Specify outer-class method, otherwise
      // you'd get an infinite recursion:
      Callee2.this.increment();
    }
  }
  Incrementable getCallbackReference() {
    return new Closure();
  }
}	

class Caller {
  private Incrementable callbackReference;
  Caller(Incrementable cbh) { callbackReference = cbh; }
  void go() { callbackReference.increment(); }
}

public class Callbacks {
  public static void main(String[] args) {
    Callee1 c1 = new Callee1();
    Callee2 c2 = new Callee2();
    MyIncrement.f(c2);
    Caller caller1 = new Caller(c1);
    Caller caller2 = new Caller(c2.getCallbackReference());
    caller1.go();
    caller1.go();
    caller2.go();
    caller2.go();
  }	
} /* Output:
Other operation
1
1
2
Other operation
2
Other operation
3
*///:~

不难看出,private class Closure implements Incrementable是Callee2的内部类,且实现了Incrementable,同时Callee2继承自MyIncrement,其中接口Incrementable和类MyIncrement都有一个public void increment()方法,我们来假设这样一个应用场景:要求既要实现Incrementable接口又要继承MyIncrement且重写increment(),可以想象在没有内部类的帮助下是很难实现的,而上面的这段代码做到了,更神奇的是在一个类中有两个public void increment(),而且外部类中的increment()方法对内部类中的increment()没有任何影响,同时

 Callee2.this

会指向外部类的对象,所以

  Callee2.this.increment();

指向的就是外部类的increment()方法。

总结:内部类的引入是java不能多继承的替代方案,它可以使代码更加灵活简洁安全,每一个内部类都有一个指向外部类的“指针”(.this),使它可以任意引用外部类的所有属性和方法,更重要的是,外部类的改变对内部类没有丝毫影响,更确切的说内部类是外部类的“窗口”。

注:以上代码和部分概念引用自《THINKING IN JAVA 4 》(191-215)




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值