java闭包详解

1、为何须要闭包java

  • 闭包的价值在于能够做为函数对象或者匿名函数,持有上下文数据,做为第一级对象进行传递和保存。
  • 闭包普遍用于回调函数、函数式编程中。

2、闭包的概念编程

  • 闭包是一个可调用的对象,它记录了一些信息,这些信息来自于建立他的做用域,用过这个定义 能够看出内部类是面向对象的闭包 由于他不只包含外围类对象的信息 还自动拥有一个指向此外围类对象的引用 在此做用域内 内部类有权操做全部的成员 包括private成员;interfaceIncrementable。 (出自 java编程思想)
  • java中的内部类 采用 ”接口 + 内部类“ 实现。

3、java 内部类的几种表现形式闭包

内部类ide

     在JAVA中,内部类能够访问到外围类的变量、方法或者其它内部类等全部成员,即便它被定义成private了,可是外部类不能访问内部类中的变量。这样经过内部类就能够提供一种代码隐藏和代码组织的机制,而且这些被组织的代码段还能够自由地访 问到包含该内部类的外围上下文环境。函数式编程

public class DemoClass{
private int length =0;

private class InnerClass implements ILog
{
    @Override
    public void Write(String message) {          
        System.out.println("DemoClass.InnerClass:" + length);
    }
}

public ILog logger() {
    return new InnerClass();
}

public static void main(String[] args){
    DemoClass demoClass = new DemoClass();
    demoClass.logger().Write("abc");

    //new
    DemoClass dc = new DemoClass();
    InnerClass ic = dc.logger();
    ic.Write("abcde");
}
}

从上可见,InnerClass是定义在DemoClass内部的一个内部类,并且InnerClass还能够是Private。函数

  • 如何经过this显式引用外围类的变量? * 经过此格式进行引用:{外围类名}.this.{变量名称}。如: DemoClass. .length = message.length();

局部内部类this

     局部内部类是指在方法的做用域内定义的的内部类。spa

public class DemoClass2 {
    private int length =0;

    public ILog logger() {
        //在方法体的做用域中定义此局部内部类
        class InnerClass implements ILog
        {
            @Override
            public void Write(String message) {
                length = message.length();
                System.out.println("DemoClass2.InnerClass:" + length);
            }
        }
        return new InnerClass();
    }
}

由于InnerClass类是定义在logger()方法体以内,因此InnerClass类在方法的外围是不可见的。日志

匿名内部类code

    匿名内部类就是匿名、没有名字的内部类,经过匿名内部类能够更加简洁的建立一个内部类。

public class DemoClass3 {
    private int length =0;

    public ILog logger() {
       return new ILog() {
            @Override
            public void Write(String message) {
                  length = message.length();
                  System.out.println("DemoClass3.AnonymousClass:" + length);
            }
       };
    }
}

因而可知,要建立一个匿名内部类,能够new关键字来建立。

  • 格式:new 接口名称(){}

  • 格式:new 接口名称(args…){}

final关键字

  闭包所绑定的本地变量必须使用final修饰符,以表示为一个恒定不变的数据,建立后不能被更改。

public class DemoClass4 {
    private int length =0;

    public ILog logger(int level) {//final int level
        //final
        final int logLevel = level+1;

        switch(level)
        {
            case 1:
                return new ILog() {
                    @Override
                    public void Write(String message) {
                        length = message.length();
                        System.out.println("DemoClass4.AnonymousClass:InfoLog " + length);
                        System.out.println(logLevel);
                    }
                };    
            default:
            return new ILog() {
                @Override
                public void Write(String message) {
                    length = message.length();
                    System.out.println("DemoClass4.AnonymousClass:ErrorLog " + length);
                    System.out.println(logLevel);
                }
            };

        }
    }

    public static void main(String[] args){
        DemoClass4 demoClass4 = new DemoClass4();
        demoClass4.logger(1).Write("abcefghi");
    }

}

从例子中能够看到,logger方法接受了一个level参数,以表示要写的日志等级,这个level参数若是直接赋给内部类中使用,会致使编译时错误,提示level参数必须为final,这种机制防止了在闭包共享中变量取值错误的问题。解决方法能够像例子同样在方法体内定义一下新的局部变量,标记为final,而后把参数level赋值给它:

final int logLevel = level ;
  //或者直接参数中添加一个final修饰符:
  public ILog logger(final int level {

实例初始化

    匿名类的实例初始化至关于构造器的做用,但不能重载。

public ILog logger(final int level) throws Exception {

        return new ILog() {
            {
                //实例初始化,不能重载 
                if(level !=1)
                    throw new Exception("日志等级不正确!");
            }

            @Override
            public void Write(String message) {
                length = message.length();
                System.out.println("DemoClass5.AnonymousClass:" + length);
            }
        };
    }

匿名内部类的实例初始化工做能够经过符号 {…} 来标记,能够在匿名内部类实例化时进行一些初始化的工做,可是由于匿名内部类没有名称,因此不能进行重载,若是必须进行重载,只能定义成命名的内部类。

4、闭包的问题

  1. 让某些对象的生命周期加长。
    让自由变量的生命周期变长,延长至回调函数执行完毕。
  2. 闭包共享。
    final关键字
interface Action
{
    void Run();
}

public class ShareClosure {

    List<Action> list = new ArrayList<Action>();

    public void Input()
    {
        for(int i=0;i<10;i++)
        {
            final int copy = i;
            list.add(new Action() {    
                @Override
                public void Run() {
                    System.out.println(copy);
                }
            });
        }
    }

    public void Output()
    {
        for(Action a : list){a.Run();}
    }

    public static void main(String[] args) {
        ShareClosure sc = new ShareClosure();
        sc.Input();
        sc.Output();

    }

}

这个例子建立一个接口列表List ,先向列表中建立 i 个匿名内部类new Action(),而后经过for遍历读出。 由于 i 变量在各个匿名内部类中使用,这里产生了闭包共享,java编译器会强制要求传入匿名内部类中的变量添加final 关键字,因此这里final int copy = i;须要作一个内存拷贝,不然编译不过。

相关文章
相关标签/搜索