zoukankan      html  css  js  c++  java
  • 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();
        }
    } // /:~
  • 相关阅读:
    正则表达式转换python2的print为python3风格
    译:Local Spectral Graph Convolution for Point Set Feature Learning-用于点集特征学习的局部谱图卷积
    Objectbox Box的getAll() 函数返回emptylist() 未判断导致崩溃
    关于定义顺序和内存分配的关系--记一道不严谨的C语言题
    iRecognizer号码扫描开发实录
    我的visual studio 配色方案 Rubik c++版
    Opencv4android的Android Studio环境配置及项目实例下载
    随便记录下
    linux下在先安装32位qt 链接
    python笔记
  • 原文地址:https://www.cnblogs.com/liaokailin/p/3659301.html
Copyright © 2011-2022 走看看