内部类

第10章 内部类

可以将一个类的定义放在另一个类的定义内部,这就是内部类。

内部类允许我们将一些逻辑相关的类组织在一起,并控制位于内部的类的可视性。

10.1 创建内部类

创建内部类的方式就是将类的定义置于外围类的内部:

public class Parcel1 {
    class Contents {
        private int i = 11;
        public int value() { return i; }
    }
    class Destination {
        private String label;
        Destination(String wheleTo) { label = wheleTo; }
        String readLabel() { return label; }
    }

    public void ship(String dest) {
        Contents c = new Contents();
        Destination d = new Destination(dest);
        System.out.println(d.readLabel());
    }

    public static void main(String[] args) {
        Parcel1 p = new Parcel1();
        p.ship("Tasmania");
    }
}

可以看到,在ship()方法里使用内部类的时候,与使用普通类没有区别,只是内部类的名字是嵌套在Parcel1里面的。 下面的例子则会展示具体的区别:

public class Parcel2 {
    class Contents {
        private int i = 11;
        public int value() { return i; }
    }
    class Destination {
        private String label;
        Destination(String wheleTo) { label = wheleTo; }
        String readLabel() { return label; }
    }

    public Destination to(String s) {
        return new Destination(s);
    }
    public Contents contents() {
        return new Contents();
    }

    public void ship(String dest) {
        Contents c = contents();
        Destination d = to(dest);
        System.out.println(d.readLabel());
    }

    public static void main(String[] args) {
        Parcel2 p = new Parcel2();
        p.ship("Tasmania");
        Parcel2 q = new Parcel2();
        Contents c = q.contents();
        Destination d = q.to("Borneo");
    }
}

class Other {
    Parcel2 p = new Parcel2();
    Parcel2.Contents c1 = p.contents();
    //! Parcel2.Contents c2 = new Contents();
    //! Parcel2.Contents c2 = new Parcel2.Contents();
}

注意,在其他类的任何位置定义某个内部类必须具体指明这个对象的类型:OuterClassName.InnerClassName。 并且,无法像普通类一样直接通过new关键字去创建内部类对象。

10.2 链接到外部类

当生成一个内部类的对象时,该对象能够访问其外围对象的所有成员,并不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。 如下例所示:

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(i);
        Selector selector = sequence.selector();
        while(!selector.end()){
            System.out.print(selector.current() + " ");
            selector.next();
        }
    }
}

此例为迭代器设计模式的一个例子。Selector接口的作用:

  1. 检查序列是否到末尾。
  2. 获取当前对象。
  3. 移到序列下一个对象。

并且,我们可以通过策略设计模式,将Selector作为参数编写方法,以生成更加通用的代码。

可以发现,SequenceSelector作为一个内部类,却可以访问外围类中的private成员items。即内部类自动拥有对其外围类所有成员的访问权。

由于无法像普通类一样直接通过new去创建内部类对象,其只能在与外围类对象相关联的情况下才能被创建。即构建内部类对象时,必须有一个指向其外部类的引用。所以,当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向该外围类对象的引用,然后便可以通过该引用访问外围类的成员。

使用.this与.new

如果需要在内部类中捕获该外围类对象的引用,则可以使用OuterClassName.this:

public class DotThis {
    void f() { System.out.println("DotThis.f()"); }
    public class Inner {
        public DotThis outer() {
            return DotThis.this;
        }
    }
    public Inner inner() { return new Inner(); }
    public static void main(String[] args) {
        DotThis dt = new DotThis();
        Inner inner = dt.inner();
        inner.outer().f();
    }
}

当我们需要在其他类中创建某个内部类对象时,则需要使用.new:

public class DotNew {
    public static void main(String[] args) {
        DotThis dotThis = new DotThis();
        //! DotThis.Inner inner = new DotThis.Inner();
        DotThis.Inner inner = dotThis.new Inner();
    }
}

如上例所示,我们无法按照想象中的方式,通过引用外部类的名字去创建对象,而是必须使用外部类的对象来创建该内部类对象。即在拥有外部类对象前是无法创建内部类对象的。这也是内部类能够链接到外部类的主要原因。

下面将.new应用于Parcel的示例:

public class Parcel3 {
    class Contents {
        private int i = 11;
        public int value() { return i; }
    }
    class Destination {
        private String label;
        Destination(String wheleTo) { label = wheleTo; }
        String readLabel() { return label; }
    }

    public static void main(String[] args) {
        Parcel3 p = new Parcel3();
        Contents c = p.new Contents();
        Destination d = p.new Destination("Tasmania");
    }
}

但是,如果创建的是静态内部类,那么就不需要外部类对象的引用了。

10.4 内部类与向上转型

内部类的价值在于:它可以实现某个接口,并将其向上转型为基类接口。

class Parcel4 {
    private class PContents implements Contents {
        private int i = 11;
        public int value() { return i; }
    }
    protected class PDestination implements Destination {
        private String label;
        public 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");
        //! Parcel4.PContents pc = p.new PContents();
    }
}

上述代码将内部类限制为private,则该内部类完全不可见,并且外部只能获得一个指向基类或接口的引用,从而轻易地隐藏实现细节。

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

前面介绍了一些普通的内部类。下面介绍的是可以在一个方法里面或任意作用域内定义的内部类。 这么做有两个理由:

  • 实现某类型的接口,创建并返回该类型的引用。
  • 需要一个类来辅助解决一个复杂问题,并且希望该类被隐藏起来。

第一个例子展示了在方法的作用域内创建一个完整的类,这被称作局部内部类:

public class Parcel5 {
    public Destination destination(String s) {
        class PDestination implements Destination{
            private String label;
            private PDestination(String wheleTo) { label = wheleTo; }
            public String readLabel() { return label; }
        }
        return new PDestination(s);
    }
    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("Tasmania");
    }
}

可以看出,PDestination类是作为destination()方法的一部分,而不是Parcel5的一部分。即该内部类在目标方法之外是无法被访问的,但并不是意味着一旦目标方法执行完毕,该类就不可用了。

下面的例子展示了如何在任意的作用域内嵌入一个内部类:

public class Parcel6 {
    private void internalTracking(boolean b){
        if(b){
            class TrankingSlip {
                private String id;
                TrankingSlip(String s) {
                    id = s;
                }
                String getSlip() { return id; }
            }
            TrankingSlip ts = new TrankingSlip("slip");
            String s = ts.getSlip();
        }
        // Out of scope,can't use it! 
        //! TrankingSlip ts = new TrankingSlip("x");
    }
    public void track() { internalTracking(true); }
    public static void main(String[] args) {
            Parcel6 p = new Parcel6();
            p.track();
    }
}

上例中的内部类被嵌入在if语句的作用域内,并且,我们发现仅能在定义它的作用域的使用它,在此作用域之外,它是不可用的。除此之外,它与普通类一样。

匿名内部类

下面的例子看上去则和上述的内部类有所不同:

public class Parcel7 {
    public Contents contents() {
        return new Contents() {
                private int i = 11;
            public int value() { return i; }
        };
    }
    public static void main(String[] args) {
            Parcel7 p = new Parcel7();
            Contents c = p.contents();
    }
}

上述表达式,在定义了一个匿名类的同时创建类的对象。即创建了一个实现自某接口的匿名类的对象。

而下述代码似乎是其具体化形式:

public class Parcel7b {
    class MyContents implements Contents {
            private int i = 11;
        public int value() { return i; }
    }
    public Contents contents() {
            return new MyContents(); 
    }
    public static void main(String[] args) {
            Parcel7b p = new Parcel7b();
            Contents c = p.contents();
    }
}

上述的匿名内部类是实现了某接口,下面的示例则是通过这种形式返回一个继承自某个具体类的导出类对象:

public class Parcel8 {
    public Wrapping wrapping(int x) {
        return new Wrapping(x){
            public int value() {
                return super.value() * 66;
            }
        };
    }
    public static void main(String[] args) {
            Parcel8 p = new Parcel8();
            Wrapping wrapping = p.wrapping(8);
            System.out.println(wrapping.value());;
    }
}

在匿名内部类后的分号与别处的作用相同,都标记的是表达式的结束。

如果定义一个匿名内部类,并希望它使用在外部定义的变量,那么编译器会要求接收该变量的形参被定义为final:

public class Parcel9 {
    public Destination destination(final String dest) {
        return new Destination() {
            private String label = dest;
            public String readLabel() {
                return label;
            }
        };
    }
    public static void main(String[] args) {
        Parcel9 p = new Parcel9();
        Destination d = p.destination("Tasmania");
    }
}

由于匿名内部类没有名字,所以它也无法拥有构造器,但可以通过实例初始化实现构造器的效果:

abstract class Base {
    public Base(int i) {
        print("Base constructor, i = " + i);
    }
    public abstract void f();
}

public class AnonymousConstructor {
    public static Base getBase(int i){
        return new Base(i) {
            {
                print("Inside instance initializer");
            }
            public void f() {
                print("In anoymous f()");
            }
        };
    }
    public static void main(String[] args) {
        Base base = getBase(66);
        base.f();
    }
}

我们发现,此例中的变量i被未被强制限制为final,是因为该变量是被传递给匿名类的基类构造器,它并不未在匿名类内部被直接使用。

下例是带实例初始化的Pacel,其参数由于在匿名类内部使用而被强制限制为fianl:

public class Parcel10 {
    public Destination destination(final String dest,final float price) {
        return new Destination() {
            private int cost;
            private String label = dest;
            {
                cost = Math.round(price);
                if(cost > 100) 
                    System.out.println("Over budget!");
            }
            public String readLabel() {
                return label;
            }
        };
    }
    public static void main(String[] args) {
        Parcel10 p = new Parcel10();
        Destination d = p.destination("Tasmania",101.395f);
    }
}

匿名内部类与正规的继承相比有些受限:虽然匿名内部类既可以扩展类,也可以实现接口,但无法两者兼备。而且如果是实现接口,也只能实现一个接口。

10.6.1 再访工厂方法

下面通过使用匿名内部类的方式重写之前的ServiceFactories.java示例:

interface Service {
    void f1();
    void f2();
}
interface ServiceFactory {
    Service getService();
}

class ServiceImpl1 implements Service {
    private ServiceImpl1() { }
    public void f1() { print("ServiceImpl1.f1()"); }
    public void f2() { print("ServiceImpl1.f2()"); }
    public static ServiceFactory factory = new ServiceFactory() {
        public Service getService() {
            return new ServiceImpl1();
        }
    };
}
class ServiceImpl2 implements Service {
    private ServiceImpl2() { }
    public void f1() { print("ServiceImpl2.f1()"); }
    public void f2() { print("ServiceImpl2.f2()"); }
    public static ServiceFactory factory = new ServiceFactory() {
        public Service getService() {
            return new ServiceImpl2();
        }
    };
}

public class ServiceFactories {
    public static void serviceConsumer(ServiceFactory factory) {
        Service service = factory.getService();
        service.f1();
        service.f2();
    }

    public static void main(String[] args) {
        serviceConsumer(ServiceImpl1.factory);
        serviceConsumer(ServiceImpl2.factory);
    }
}

此时,我们可以将Service的两个导出类构造器设为private,从而限制外界只能使用工厂来创建实例。并且通过将工厂对象设置为static使其成为单例。

Games实例也可通过匿名内部类进行改进:

interface Game {
    boolean move();
}
interface GameFactory {
    Game getGame();
}

class Checkers implements Game {
    private int              moves = 0;
    private final static int MOVES = 3;
    private Checkers() {}
    public static GameFactory factory = new GameFactory() {
        public Game getGame() {
            return new Checkers();
        }
    };
    public boolean move() {
        System.out.println("Checkers move " + moves);
        return ++moves != MOVES;
    }
}
class Chess implements Game {
    private int              moves = 0;
    private final static int MOVES = 4;
    private Chess() {}
    public static GameFactory factory = new GameFactory() {
        public Game getGame() {
            return new Chess();
        }
    };
    public boolean move() {
        System.out.println("Chess move " + moves);
        return ++moves != MOVES;
    }
}

public class Games {
    public static void playGame(GameFactory factory) {
        Game game = factory.getGame();
        while (game.move())
                ;
    }

    public static void main(String[] args) {
        playGame(Checkers.factory);
        playGame(Chess.factory);
    }
}

10.7 嵌套类

如果不需要内部类对象与其外围类对象之间有联系,则可以将内部类声明为static,即嵌套类。通过前面的学习,我们知道普通的内部类对象隐式地保存了一个指向创建它的外围对象的引用。而嵌套类则不是这样:

  • 要创建嵌套类的对象,并不需要其外围类的对象。
  • 不能从嵌套类的对象中访问非静态的外围类成员。

嵌套类还有一个特点就是可以拥有静态成员:

public class Parcel11 {
    private static class ParcelContents implements Contents {
        private int i = 11;
        public int value() { return i; }
    }
    protected static class ParcelDestination implements Destination {
        private String label;
        private ParcelDestination(String whereTo) {
            label = whereTo;
        }
        public String readLabel() { return label; }
        public static void f() {}
        static int x = 16;
        static class AnotherLevel {
            public static void f() {}
            static int x = 19;
        }
    }
    public static Destination destination(String s){
        return new ParcelDestination(s);
    }
    public static Contents contents(){
        return new ParcelContents();
    }
    public static void main(String[] args) {
        Destination d = destination("Tasmania");
        Contents c = contents();
        System.out.println(ParcelDestination.x);
        System.out.println(Parcel11.ParcelDestination.AnotherLevel.x);;
    }
}

我们可以看到创建嵌套类对象时并不需要外围类对象,而是通过使用静态方法返回嵌套类的对象。并且在嵌套类内部也无法像普通内部类那样通过this获取其外围类对象的引用。

10.7.1 接口内部的类

由于接口中的任何成员都自动地是public和static的,所以嵌套类也可作为接口的一部分。并且可以使用嵌套类实现其外围接口

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();
        }
    }
}

通过使用接口内部的嵌套类可以使其被该接口的所有不同的实现所共用。

10.7.2 从多层嵌套类中访问外部类的成员

一个普通的内部类中无论嵌套了多少层,里层的类总能透明地访问其外围类的成员,而不管其是否是private

class MNA {
    private void f() {}
    class A {
        private void g() {}
        public class B {
            void h() {
                g();
                f();
            }
        }
    }
}
public class MultiNestingAccess {
    public static void main(String[] args) {
        MNA mna = new MNA();
        MNA.A mnaa = mna.new A();
        MNA.A.B mnaab = mnaa.new B();
        mnaab.h();
    }
}

上例同时展示了如何从不同的类里创建多层嵌套的内部类对象的基本语法。

10.8 为什么需要内部类

内部类最吸引人的原因就是:它具有可以继承多个具体的或抽象的类的能力。

我们考虑一个情形:必须在一个类中以某种方式实现两个接口。

interface A {}
interface B {}

class X implements A,B {}
class Y implements A {
    B makeB() {
        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 d) {}
    public static void main(String[] args) {
        Z z = new Z();
        takesD(z);
        takesE(z.makeE());
    }
}

除了多重继承外,内部类还有一些其他特性:

  • 内部类可以拥有多个实例,每个实例都有自己的信息状态,并且与其外围对象的信息相互独立。
  • 在单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。
  • 创建内部类对象的时刻并不依赖于外围类对象的创建。
  • 内部类是一个独立的实体,不存在is-a关系。

10.8.1 闭包和回调

闭包:一个可调用的对象记录一些来自于创建它的作用域的信息。我们可以发现,内部类不仅包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private成员。

通过内部类提供闭包的功能是优良的解决方案:

interface Incrementable {
    void increment();
}

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(); }
}

class Callee2 extends MyIncrement {
    private int i = 0;
    public void increment() {
        super.increment();
        i++;
        print(i);
    }
    private class Closure implements Incrementable {
        public void increment() {
            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();
    }
}

由于Callee2继承了MyIncrement,并且该基类与其需要实现的接口的方法同名,但作用完全不相关,所以为了保留基类方法的用途,我们只能通过内部类的方式实现该接口,并通过一个方法将内部类对象的引用传递出去。从而使得Callee2保留了基类中该方法的功能,并实现了该接口。

10.8.2

应用程序框架是被设计用以解决某类特定问题的一个类或一组类,而要运用某个应用程序框架,通常需要继承一个或多个类,并覆盖某些方法。在覆盖后的方法中,编写代码定制应用程序框架提供的通用解决方案,以解决特定问题。

控制框架是一类特殊的应用程序框架,它用来解决响应时间的需求。

下面的接口描述了要控制的事件:

public abstract class Event {
    private long eventTime;
    protected final long delayTime;
    public Event(long delayTime) {
        this.delayTime = delayTime;
        start();
    }
    public void start() {
        eventTime = System.nanoTime() + delayTime;
    }
    public boolean ready() {
        return System.nanoTime() >= eventTime;
    }
    public abstract void action();
}

下面是一个用来管理并触发事件的实际控制框架:

public class Controller {
    private List<Event> eventList = new ArrayList<Event>();
    public void addEvent(Event e) { eventList.add(e); }
    public void run() {
        for (Event event : new ArrayList<Event>(eventList)) {
            if(event.ready()){
                System.out.println(event);
                event.action();
                eventList.remove(event);
            }
        }
    }
}

注意,在目前的设计中,我们并不知道Event到底做了什么。这就是此设计的关键所在:使变化的事物与不变的事物相互分离。而框架的使用者可以通过特定需求,创建不同的Event子类去表现不同的行为。

而内部类的特性正好使其成为创建Event子类的最佳方式:

  • 在一个普通类中可以拥有多个内部类去实现某个特定类,从而得到该类不同的子类,并将实现的细节封装起来。
  • 内部类可以很容易地访问其外围类的任意成员,从而方便的共用某个或多个外围类成员。

下面是该控制框架的一个特定实现:控制灯光、水、温度调节器的开关以及响铃和重新启动系统。控制框架的设计使得实现该系统变得容易,每种行为都是一个Event子类:

public class GreenhouseControls extends Controller{
    private boolean light = false;
    public class LightOn extends Event {
        public LightOn(long delayTime) { super(delayTime); }
        public void action() {
            light = true;
        }
        public String toString() { return "Light is on"; }
    }
    public class LightOff extends Event {
        public LightOff(long delayTime) { super(delayTime); }
        public void action() {
            light = false;
        }
        public String toString() { return "Light is off"; }
    }
    private boolean water = false;
    public class WaterOn extends Event {
        public WaterOn(long delayTime) { super(delayTime); }
        public void action() {
            water = true;
        }
        public String toString() { return "Greenhouse water is on"; }
    }
    public class WaterOff extends Event {
        public WaterOff(long delayTime) { super(delayTime); }
        public void action() {
            water = false;
        }
        public String toString() { return "Greenhouse water is off"; }
    }
    private String thermostat = "Day";
    public class ThermostatNight extends Event {
        public ThermostatNight(long delayTime) { super(delayTime); }
        public void action() {
            thermostat = "Night";
        }
        public String toString() { return "Thermostat on night setting"; }
    }
    public class ThermostatDay extends Event {
        public ThermostatDay(long delayTime) { super(delayTime); }
        public void action() {
            thermostat = "Day";
        }
        public String toString() { return "Thermostat on day setting"; }
    }
    public class Bell extends Event {
        public Bell(long delayTime) { super(delayTime); }
        public void action() {
            addEvent(new Bell(delayTime));
        }
        public String toString() { return "Bing!"; }
    }
    public class Restart extends Event {
        private Event[] eventList;
        public Restart(long delayTime,Event[] eventList) {
            super(delayTime);
            this.eventList = eventList;
            for (Event event : eventList) 
                addEvent(event);
        }
        public void action() {
            for (Event event : eventList) {
                event.start();
                addEvent(event);
            }
            start();
            addEvent(this);
        }
        public String toString() { return "Restarting system"; }
    }
    
    public static class Terminate extends Event {
        public Terminate(long delayTime) { super(delayTime); }
        public void action() { System.exit(0); }
        public String toString() { return "Terminating"; }
    }
}

下面是一个命令设计模式的例子,通过将每个请求都封装成一个对象放在集合中:

public class GreenhouseController {
    public static void main(String[] args) {
        GreenhouseControls gc = new GreenhouseControls();
        gc.addEvent(gc.new Bell(900));
        Event[] events = {
            gc.new ThermostatNight(0),
            gc.new LightOn(200),
            gc.new LightOff(400),
            gc.new WaterOn(600),
            gc.new WaterOff(800),
            gc.new ThermostatDay(1400)
        };
        gc.addEvent(gc.new Restart(2000, events));
        if(args.length == 1)
            gc.addEvent(new GreenhouseControls.Terminate(new Integer(args[0])));
        gc.run();
    }
}

从上述的示例中,我们可以看出内部类在控制框架中的价值。

10.9 内部类的继承

由于创建内部类必须需要其外围类对象的引用,所以,当一个普通类继承内部类的时候,必须在其构造器中传递该内部类的外围类对象的引用:

class WithInner {
    class Inner  {}
}
public class InheritInner extends WithInner.Inner {
    public InheritInner(WithInner wi) {
        wi.super();
    }
    public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
    }
}

10.10 内部类可以被覆盖吗

如果A类中有一个内部类B,而C类作为A类的导出类,也在其内部重新定义了此内部类B,是否会发送覆盖效果呢?

class Egg {
    private Yolk y;
    protected class Yolk {
        public Yolk() {
            print("Egg.Yolk()");
        }
    }
    public Egg() {
        print("New Egg()");
        y = new Yolk();
    }
}
public class BigEgg extends Egg{
    public class Yolk {
        public Yolk() {
            print("BigEgg.Yolk()");
        }
    }
    public static void main(String[] args) {
        new BigEgg();
    }
}

上例说明:当继承了某个外围类的时候,即使导出类和基类中都含有某个同名的内部类,但它们依然是两个完全独立的实体,并各自在其自己的命名空间内。

10.11 局部内部类

前面提到在方法和作用域的局部内部类,下面的例子对它和匿名内部类进行了对比:

interface Counter {
    int next();
}
public class LocalInnerClass {
    private int count = 0;
    Counter getCounter(final String name) {
        class LocalCounter implements Counter {
            public LocalCounter() {
                print("LocalCounter()");
            }
            public int next() {
                printnb(name);
                return count++;
            }
        }
        return new LocalCounter();
    }
    Counter getCounter2(final String name) {
        return new Counter() {
            {
                print("Counter()");
            }
            public int next() {
                printnb(name);
                return count++;
            }
        };
    }
    public static void main(String[] args) {
        LocalInnerClass lic = new LocalInnerClass();
        Counter 
                c1 = lic.getCounter("Local inner"),
                c2 = lic.getCounter2("Anonymous inner");
        for (int i = 0; i < 5; i++) 
            print(c1.next());
        for (int i = 0; i < 5; i++) 
            print(c2.next());
    }
}

通过上例,我们发现:局部内部类和匿名内部类具有相同的行为和能力。相比之下,匿名内部类更为方便,但局部内部类具有匿名内部类没有的功能以满足我们的需求:

  • 需要一个已命名的构造器,或需要重载构造器。
  • 需要不止一个该内部类的对象。

10.12 内部类标识符

由于每个类都会产生一个.class文件,而内部类生成的文件名称为:OuterClassName + $ + InnerClassName。 例如,上例的LocalInnerClass.java生成的.class文件包括:

  • Counter.class
  • LocalInnerClass$1.class
  • LocalInnerClass$1LocalCounter.class
  • LocalInnerClass.class

如果是匿名内部类,编译器会简单地产生一个数字作为其标识符。如果内部类是嵌套在别的内部类之中,只需直接将它们的名字加在其外围类标识符与$的后面。

10.13 总结

接口和内部类比起面向对象编程中其他的概念来说更深奥复杂,但它们的作用也十分强大,尤其是能够帮助我们解决多重继承问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值