02-深入内部类

一、前言

  可将一个类定义置入另一个类定义中,这就叫作“内部类”。内部类对我们非常有用,因为利用它可对那些逻辑上相互联系的类进行分组,并可控制一个类在另一个类里的“可见性”  

二、内部类的定义:

public class Parcel1 {
    class Contents {
        private int i = 11;

        public int value() {
            return i;
        }
    }

    class Destination {
        private String label;

        Destination(String whereTo) {
            label = whereTo;
        }

        String readLabel() {
            return label;
        }
    }

    // Using inner classes looks just like
    // using any other class, within Parcel1:
    public void ship(String dest) {
        Contents c = new Contents();
        Destination d = new Destination(dest);
    }

    public static void main(String[] args) {
        Parcel1 p = new Parcel1();
        p.ship("Tanzania");
    Parcel1.Contents c = p.new Contents() ; } }
// /:~

   注意创建内部类对象的方式  

三、在方法或者作用域中使用内部类

  3.1在一个方法或者任意的作用域中使用内部类一般有两个目的: 

     1:准备实现某种形式的接口,使自己的程序能创建或者返回一个句柄

              2:  要解决一个复杂的问题,并希望创建一个类用来辅助自己的程序方案 同时又不愿意把它公开

     为目的1来一个例子:  

          

//: Parcel3.java
// Returning a handle to an inner class
package c07.parcel3;

abstract class Contents {
    abstract public int value();
}

interface Destination {
    String readLabel();
}

public class Parcel3 {
    private class PContents extends 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 dest(String s) {
        return new PDestination(s);
    }

    public Contents cont() {
        return new PContents();
    }
}

class Test {
    public static void main(String[] args) {
        Parcel3 p = new Parcel3();
        Contents c = p.cont();
        Destination d = p.dest("Tanzania");
        // Illegal -- can't access private class:
        // ! Parcel3.PContents c = p.new PContents();
    }
} // /:~

 

       3.2 在一个方法内创建内部类

        为例子的需要先创建两个接口: 

//: Destination.java
package c07.innerscopes;
interface Destination {
String readLabel();
} ///:~


//: Contents.java
package c07.innerscopes;
interface Contents {
int value();
} ///:~


//: Wrapping.java
package c07.innerscopes;
public class Wrapping {
private int i;
public Wrapping(int x) { i = x; }
public int value() { return i; }
} ///:~

 

         在一个方法中定义一个内部类  :

//: Parcel4.java
// Nesting a class within a method
package c07.innerscopes;

 
public class Parcel4 {
    
    public Destination dest(String s) {
        //在方法的一个作用域内定义的类
        class PDestination implements Destination {
            private String label;

            private PDestination(String whereTo) {
                
                label = whereTo;
                //TODO handle label 
            }

            public String readLabel() {
                return label;
            }

        }
        return new PDestination(s);
    }

    public static void main(String[] args) {
        Parcel4 p = new Parcel4();
        Destination d = p.dest("Tanzania");
        System.out.println(d.readLabel()); ;
    }
} // /:~

 

    方法dest需要返回一个Destination 类型的对象,通常的操作是在方法外部(在同一个类中定义一个内部类或者新增加一个类)实现一个Destination 接口的类 然后在dest方法中返回该类对象

  这样子做的话很显然会将该类的实现暴漏在dest方法外部 如果操作只对dest方法可见(或者说是唯一针对该方法的操作) 那么在类中定义一个内部类很有必要。当然,不能由于类PDestination 的名字置于

dest()内部,就认为在dest()返回之后PDestination 不是一个有效的对象。

          3.3在任意的作用域内定义内部类

           

//: Parcel5.java
// Nesting a class within a scope
package c07.innerscopes;

public class Parcel5 {
    private void internalTracking(boolean b) {
        if (b) {
            class TrackingSlip {
                private String id;

                TrackingSlip(String s) {
                    id = s;
                }

                String getSlip() {
                    return id;
                }
            }
            TrackingSlip ts = new TrackingSlip("slip");
            String s = ts.getSlip();
            System.out.println(s);
        }
        // Can't use it here! Out of scope:
        // ! TrackingSlip ts = new TrackingSlip("x");
    }

    public void track() {
        internalTracking(true);
    }

    public static void main(String[] args) {
        Parcel5 p = new Parcel5();
        p.track();
    }
} // /:~

 

    TrackingSlip 类嵌套于一个if 语句的作用域内。这并不意味着类是有条件创建的——它会随同其他所有东西得到编译。然而,在定义它的那个作用域之外,它是不可使用的。除这些以外,它看起来和

一个普通类并没有什么区别。

          3.4 匿名内部类

        

//: Parcel6.java
// A method that returns an anonymous inner class
package c07.innerscopes;

public class Parcel6 {
    public Contents cont() {
        return new Contents() {
            private int i = 11;

            public int value() {
                return i;
            }
        }; // Semicolon required in this case
    }

    public static void main(String[] args) {
        Parcel6 p = new Parcel6();
        Contents c = p.cont();
        System.out.println(c.value());
    }
} // /:~

 

      在匿名内部类中,Contents 是用一个默认构建器创建的,那么如果需要基础类中构造方法含有参数又如何是实现呢? 

//: Parcel7.java
// An anonymous inner class that calls the
// base-class constructor
package c07.innerscopes;

public class Parcel7 {
    public Wrapping wrap(int x) {
        // Base constructor call:
        return new Wrapping(x) {
            public int value() {
                return super.value() * 47;
            }
        }; // Semicolon required
    }

    public static void main(String[] args) {
        Parcel7 p = new Parcel7();
        Wrapping w = p.wrap(10);

    }
} // /:~

 

     要注意的是:匿名内部类既然是匿名的 那么肯定是没有构造方法的(匿名内部类没有构造方法) 那么一个新的问题又来了,没有构造方法 如何初始化呢? 

//: Parcel8.java
// An anonymous inner class that performs
// initialization. A briefer version
// of Parcel5.java.
package c07.innerscopes;

public class Parcel8 {
    // Argument must be final to use inside
    // anonymous inner class:
    public Destination dest(final String dest) {
        return new Destination() {
            private String label = dest;

            public String readLabel() {
                return label;
            }
        };
    }

    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Destination d = p.dest("Tanzania");
    }
} // /:~

 

       通过上面的例子知道: 可在定义自己的字段时进行初始化,但想使用在匿名内部类外部定义的一个对象,则编译器要求外部对象为final属性。不过新的问题又来了 按照上面的初始化只能对简单

类的属性进行初始化 ,如果要想进行一些逻辑操作的话 上面那种方法显然不适用,此时又如何处理? 联系到第一节讲的初始化顺序 可以通过一个普通代码块来模拟一个构造代码块

   

//: Parcel9.java
// Using "instance initialization" to perform
// construction on an anonymous inner class
package c07.innerscopes;

public class Parcel9 {
    public Destination dest(final String dest, final float price) { 
        return new Destination() {
            private int cost;
            // Instance initialization for each object:
            {     //普通代码块来模拟一个构造代码块
                System.out.println("Instance initialization for each object:");
                cost = Math.round(price);
                if (cost > 100) //逻辑判断
                    System.out.println("Over budget!");
            }

            private String label = dest;

            public String readLabel() {
                System.out.println(label);
                return label;
            }
        };
    }

    public static void main(String[] args) {
        Parcel9 p = new Parcel9();
        Destination d = p.dest("Tanzania", 101.395F);
    }
} // /:~

 

   

四、 访问外部类

        前面的例子都展示的是通过内部类来影藏或者代码组织的方法 ,但似乎并不特别引人注目。然而,我们还忽略了另一个重要的事实。创建自己的内部类时,那个类的对象同时拥有指向封装对象(这些对象封装或生成了内部类)的一个链接。所以它们能访问那个封装对象的成员——毋需取得任何资格。除此以外,内部类拥有对封装类所有元素的访问权限。

       

//: Sequence.java
// Holds a sequence of Objects
interface Selector {
    boolean end();

    Object current();

    void next();
}

public class Sequence {
    private Object[] o;
    private int next = 0;

    public Sequence(int size) {
        o = new Object[size];
    }

    public void add(Object x) {
        if (next < o.length) {
            o[next] = x;
            next++;
        }
    }

    private class SSelector implements Selector {
        int i = 0;

        public boolean end() {
            return i == o.length;
        }

        public Object current() {
            return o[i];
        }

        public void next() {
            if (i < o.length)
                i++;

        }
    }

    public Selector getSelector() {
        return new SSelector();
    }

    public static void main(String[] args) {
        Sequence s = new Sequence(10);
        for (int i = 0; i < 10; i++)
            s.add(Integer.toString(i));
        Selector sl = s.getSelector();
        while (!sl.end()) {
            System.out.println((String) sl.current());
            sl.next();
        }
    }
} // /:~

 

   五、内部类的继承问题

    由于内部类构建器必须同封装类对象的一个句柄联系到一起,所以从一个内部类继承的时候,情况会稍微变得有些复杂。这儿的问题是封装类的“秘密”句柄必须获得初始化,而且在衍生类中不再有一个默认的对象可以连接。解决这个问题的办法是采用一种特殊的语法,明确建立这种关系

//: InheritInner.java
// Inheriting an inner class
class WithInner {
class Inner {}
}
public class InheritInner extends WithInner.Inner {
//! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
   wi.super();
}
public static void main(String[] args) {
   WithInner wi = new WithInner();
   InheritInner ii = new InheritInner(wi);
}
} ///:~

 

          如果继承一个父类,父类中定义了一个内部类,那么在子类中能覆盖内部类吗?

//: BigEgg.java
// An inner class cannot be overriden
// like a method
class Egg {
    protected class Yolk {
        public Yolk() {
            System.out.println("Egg.Yolk()");

        }
    }

    private Yolk y;

    public Egg() {
        System.out.println("New Egg()");
        y = new Yolk();
    }
}

public class BigEgg extends Egg {
    public class Yolk {
        public Yolk() {
            System.out.println("BigEgg.Yolk()");
        }
    }

    public static void main(String[] args) {
        new BigEgg();
    }
} // /:~

 

    默认构建器是由编译器自动合成的,而且会调用基础类的默认构建器。大家或许会认为由于准备创建一个BigEgg,所以会使用Yolk 的“被覆盖”版本。但实际情况并非如此。输出如下:
      New Egg()
      Egg.Yolk()
    这个例子简单地揭示出当我们从外部类继承的时候,没有任何额外的内部类继续下去。然而,仍然有可能“明确”地从内部类继承:

//: BigEgg2.java
// Proper inheritance of an inner class
class Egg2 {
    protected class Yolk {
        public Yolk() {
            System.out.println("Egg2.Yolk()");
        }

        public void f() {
            System.out.println("Egg2.Yolk.f()");
        }
    }

    private Yolk y = new Yolk();

    public Egg2() {
        System.out.println("New Egg2()");
    }

    public void insertYolk(Yolk yy) {
        y = yy;
    }

    public void g() {
        y.f();
    }
}

public class BigEgg2 extends Egg2 {
    public class Yolk extends Egg2.Yolk {
        public Yolk() {
            System.out.println("BigEgg2.Yolk()");
        }

        public void f() {
            System.out.println("BigEgg2.Yolk.f()");
        }
    }

    public BigEgg2() {
     insertYolk(new Yolk());
    }

    public static void main(String[] args) {
        
        Egg2 e2 = new BigEgg2();
        e2.g();
    }
} // /:~

 

 

new BigEgg2() 执行分析:
         *  BigEgg2 extends Egg2  因此需要先执行Egg2中的变量初始化 + 构造方法:
         *                                           初始化y 打印Egg2.Yolk() 构造方法打印:New Egg2()
         *  -->接着执行      BigEgg2的构造方法  调用 new Yolk()    此时的new Yolk 调用的是BigEgg2中的Yolk类的构造方法(覆盖了父类的方法)    
         *               是调用该构造方法的时候要先调用父类的构造方法 打印Egg2.Yolk()  然后调用本身Yolk构造方法打印 BigEgg2.Yolk()  
         *  调用 e2.g() 打印的是BigEgg2中的 Yolk的f() 打印          BigEgg2.Yolk.f() 
         *  因此打印结果如下:
         *         Egg2.Yolk()                                                
           *     New Egg2()
           *     Egg2.Yolk()
           *     BigEgg2.Yolk()
           *     BigEgg2.Yolk.f()







六、控制框架

一个“应用程序框架”是指一个或一系列类,它们专门设计用来解决特定类型的问题。为应用应用程序框架,我们可从一个或多个类继承,并覆盖其中的部分方法。我们在覆盖方法中编写的代码用于定制由那些

应用程序框架提供的常规方案,以便解决自己的实际问题。“控制框架”属于应用程序框架的一种特殊类型受到对事件响应的需要的支配;主要用来响应事件的一个系统叫作“由事件驱动的系统”。在应用程序设

计语言中,最重要的问题之一便是“图形用户界面”(GUI),它几乎完全是由事件驱动的。 AWT 属于一种控制框架,它通过内部类完美地解决了GUI 的问题。为理解内部类如何简化控制框架的创建与使用,

可认为一个控制框架的工作就是在事件“就绪”以后执行它们。 请认识到针对控制框架需要控制的东西,框架内并未包含任何特定的信息。首先,它是一个特殊的接口,描述了所有控制事件。它可以是一个抽

象类,而非一个实际的接口。 

   

//: Event.java
// The common methods for any control event
package c07.controller;

abstract public class Event {
	private long evtTime;

	public Event(long eventTime) {
		evtTime = eventTime;
	}

	public boolean ready() {
		return System.currentTimeMillis() >= evtTime;
	}

	abstract public void action();

	abstract public String description();
} // /:~

  

 

//: Controller.java
// Along with Event, the generic
// framework for all control systems:
package c07.controller;

// This is just a way to hold Event objects.
class EventSet {
    private Event[] events = new Event[100];
    private int index = 0;
    private int next = 0;

    public void add(Event e) {
        if (index >= events.length)
            return; // (In real life, throw exception)
        events[index++] = e;
    }

    public Event getNext() {
        boolean looped = false;
        int start = next;   
        do {
            next = (next + 1) % events.length;  //可以循环遍历下去
            // See if it has looped to the beginning:
            if (start == next)
                looped = true;
            // If it loops past start, the list
            // is empty:

            if ((next == (start + 1) % events.length) && looped)
                return null;
        } while (events[next] == null);
        return events[next];
    }

    public void removeCurrent() {
        events[next] = null;
    }
}

public class Controller {
    private EventSet es = new EventSet();

    public void addEvent(Event c) {
        es.add(c);
    }

    public void run() {
        Event e;
        while ((e = es.getNext()) != null) {
            if (e.ready()) {
                e.action();
                System.out.println(e.description());
                es.removeCurrent();
            }
        }
    }
} // /:~

 


  设计了Event类我们仍然不能准确地知道一个“事件”要做什么。这正是整个设计的关键,它怎样“将发生变化的东西同没有变化的东西区分开”?或者用我的话来讲,“改变的意图”造成了各类Event 对象

的不同行动。我们通过创建不同的Event 子类,从而表达出不同的行动。这里正是内部类大显身手的地方。

它们允许我们做两件事情:

       (1) 在单独一个类里表达一个控制框架应用的全部实施细节,从而完整地封装与那个实施有关的所有东西。内部类用于表达多种不同类型的action(),它们用于解决实际的问题。除此以外,后续的例子

            使用了private 内部类,所以实施细节会完全隐藏起来,可以安全地修改。

   (2) 内部类使我们具体的实施变得更加巧妙,因为能方便地访问外部类的任何成员。若不具备这种能力,代码看起来就可能没那么使人舒服,最后不得不寻找其他方法解决。

 

具体实施方式,它设计用来控制温室(Greenhouse)功能 每个行动都是完全不同的:控制灯光、供水以及温度自动调节的开与关,控制响铃,以及重新启动系统。但控
制框架的设计宗旨是将不同的代码方便地隔离开。对每种类型的行动,都要继承一个新的Event 内部类,并在action() 内编写相应的控制代码。

//: GreenhouseControls.java
// This produces a specific application of the
// control system, all in a single class. Inner
// classes allow you to encapsulate different
// functionality for each type of event.
package c07.controller;

public class GreenhouseControls extends Controller {
    private boolean light = false;
    private boolean water = false;
    private String thermostat = "Day";

    private class LightOn extends Event {
        public LightOn(long eventTime) {
            super(eventTime);
        }

        public void action() {
            // Put hardware control code here to
            // physically turn on the light.
            light = true;
        }

        public String description() {
            return "Light is on";
        }
    }

    private class LightOff extends Event {
        public LightOff(long eventTime) {
            super(eventTime);
        }

        public void action() {
            // Put hardware control code here to
            // physically turn off the light.
            light = false;
        }

        public String description() {
            return "Light is off";
        }
    }

    private class WaterOn extends Event {
        public WaterOn(long eventTime) {
            super(eventTime);
        }

        public void action() {
            // Put hardware control code here
            water = true;
        }

        public String description() {
            return "Greenhouse water is on";
        }
    }

    private class WaterOff extends Event {
        public WaterOff(long eventTime) {
            super(eventTime);
        }

        public void action() {
            // Put hardware control code here
            water = false;
        }

        public String description() {
            return "Greenhouse water is off";
        }
    }

    private class ThermostatNight extends Event {
        public ThermostatNight(long eventTime) {
            super(eventTime);
        }

        public void action() {
            // Put hardware control code here
            thermostat = "Night";
        }

        public String description() {
            return "Thermostat on night setting";
        }
    }

    private class ThermostatDay extends Event {
        public ThermostatDay(long eventTime) {
            super(eventTime);
        }

        public void action() {
            // Put hardware control code here
            thermostat = "Day";
        }

        public String description() {
            return "Thermostat on day setting";
        }
    }

    // An example of an action() that inserts a
    // new one of itself into the event list:
    private int rings;

    private class Bell extends Event {
        public Bell(long eventTime) {
            super(eventTime);
        }

        public void action() {
            // Ring bell every 2 seconds, rings times:
            System.out.println("Bing!");
            if (--rings > 0)
                addEvent(new Bell(

                System.currentTimeMillis() + 2000));
        }

        public String description() {
            return "Ring bell";
        }
    }

    private class Restart extends Event {
        public Restart(long eventTime) {
            super(eventTime);
        }

        public void action() {
            long tm = System.currentTimeMillis();
            // Instead of hard-wiring, you could parse
            // configuration information from a text
            // file here:
            rings = 5;
            addEvent(new ThermostatNight(tm));
            addEvent(new LightOn(tm + 1000));
            addEvent(new LightOff(tm + 2000));
            addEvent(new WaterOn(tm + 3000));
            addEvent(new WaterOff(tm + 8000));
            addEvent(new Bell(tm + 9000));
            addEvent(new ThermostatDay(tm + 10000));
            // Can even add a Restart object!
            addEvent(new Restart(tm + 20000));
        }

        public String description() {
            return "Restarting system";
        }
    }

    public static void main(String[] args) {
        GreenhouseControls gc = new GreenhouseControls();
        long tm = System.currentTimeMillis();
        gc.addEvent(gc.new Restart(tm));
        gc.run();
    }
} // /:~

 

 

 

posted @ 2014-04-16 17:30  廖凯林  阅读(906)  评论(0编辑  收藏  举报