[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、参考链接



posted @ 2018-01-05 14:54  Dulk  阅读(253)  评论(0编辑  收藏  举报