zoukankan      html  css  js  c++  java
  • [14-01] 闭包

    1、闭包的概念

    所谓闭包,就是指“一个持有外部环境变量的函数”,与其说这是一种形式的函数,不如说这是一种程序结构。这个概念更多在JavaScript中提到,在JS中我们知道,函数是可以作为对象返回的,于是看下面这样的方法:
    function foo(){  
        var local = 0
        function bar(){
            local++
            console.log(local)
            return local
        }
        return bar
    }

    外层函数将保存了信息的可执行内层函数作为结果返回,即这里的foo()将保存了local局部变量信息的可执行内层函数bar()作为结果返回。所以刚才我们说的概念“一个持有外部环境变量的函数”,也就是指这种结构情况下的bar()了。

    然后使用的时候可能就是这样:
    var func = foo()
    func()

    打开你的Chrome,试着把这段代码执行一下,看看它有什么用,其实就是个计数器而已,每运行一次func(),就计数一次:
     
    所以这个例子中闭包有什么用呢
    • 隐藏变量:给你的方法func()中是有变量的,但是JS中不像Java有private权限修饰,可你能访问吗,能擅自修改吗?不能

    2、Java中闭包的实现

    好了,我们借用JS来理解一下“闭包”的概念,那么Java如何实现闭包呢,Java并不支持在方法中返回一个方法呀?在JS中是外层函数包含变量和内部函数,Java中其实就是类包含成员变量和内部类。因为内部类可以直接访问外部类的信息(即便是private),这就满足了“一个持有外部环境变量的 '函数' ”:
    • 如何用变量去存储方法
      • Java中能够保存方法的变量其实就是普通的对象
    • 如何让这个对象能够访问所在类的自由变量
      • 内部类,其能够访问外部类的所有属性和方法

    那么上面那个JS的闭包计数器,用Java来写就是这样:
    public class Foo {
        //成员变量
        private int local = 0;
    
        //内部类
        class Bar {
            public int func() {
                local++;
                System.out.println(local);
                return local;
            }
        }
    
        //返回一个内部类的引用
        public Bar getBar() {
            return new Bar();
        }
    
    }
    public class Test {
        public static void main(String[] args) {
            Foo.Bar bar = new Foo().getBar();
            bar.func(); //打印1
            bar.func(); //打印2
            bar.func(); //打印3
        }
    }

    更多时候我们采用“接口+匿名内部类”的形式,面向接口更加灵活(给外部留出通道,即可以接收这个内部类),同时更好地隐藏内部类,所以我们再修改一下:
    public interface Bar {
        int func();
    }
    public class Foo {
        //成员变量
        private int local = 0;
    
        //匿名内部类
        class Inner implements Bar {
            @Override
            public int func() {
                local++;
                System.out.println(local);
                return local;
            }
        }
    
        //返回一个匿名内部类的引用
        public Bar getBar() {
            return new Inner();
        }
    
    }
    public class Test {
        public static void main(String[] args) {
            Bar bar = new Foo().getBar();
            bar.func();
            bar.func();
            bar.func();
        }
    }

    3、一个示例

    Teachable接口和Programmer基类都提供了work方法,现在有个人既是老师也是程序员,即他要继承Programmer类又要实现Teachable接口,如何处理work方法同名的问题?闭包。实际上,下面的例子稍作修改,也同样适用于委婉的多继承。
    public interface Teachable {
        public void work();
    }
    public class Programmer {
        private String name;
    
        public Programmer() {
        }
    
        public Programmer(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public void work() {
            System.out.println(name + " is coding right now.");
        }
    
    }
    public class TeachableProgrammer extends Programmer {
    
        public TeachableProgrammer() {
        }
    
        public TeachableProgrammer(String name) {
            super(name);
        }
    
        public void work() {
            super.work();
        }
    
        public void teach() {
            getCallbackReference().work();
        }
    
        private class Inner implements Teachable {
            @Override
            public void work() {
                System.out.println(TeachableProgrammer.this.getName() + " is teaching right now.");
            }
        }
    
        public Teachable getCallbackReference() {
            return new Inner();
        }
    
    }
    public class Test {
        public static void main(String[] args) {
            TeachableProgrammer person = new TeachableProgrammer("David");
            person.work();
            person.teach();
        }
    }
    
    //David is coding right now.
    //David is teaching right now.


    4、内部类的意义小结

    • 更好的封装和信息隐藏
    • 可以访问外部类的所有元素,实现闭包
    • 可以避免修改接口而实现同一个类中两种同名方法的调用
    • 间接实现多重继承


    5、参考链接



  • 相关阅读:
    让WPF和SL控件同时支持绑定和赋值
    VS2010下如何调试Framework源代码(即FCL)
    使用Entity Framework和WCF Ria Services开发SilverLight之2:POCO
    WPF快速指导15:动画
    改善C#程序的建议5:引用类型赋值为null与加速垃圾回收
    使用Entity Framework和WCF Ria Services开发SilverLight之1:简单模型
    MVVM中的命令绑定及命令参数
    改善C#程序的建议7:正确停止线程
    Prism安装、MVVM基础概念及一个简单的样例
    改善C#程序的建议8:避免锁定不恰当的同步对象
  • 原文地址:https://www.cnblogs.com/deng-cc/p/8204564.html
Copyright © 2011-2022 走看看