`

java闭包

    博客分类:
  • java
阅读更多

网上节选,未深入研究和编辑。还没空深入探讨,只是先做个笔记,待学习以后完善。

一、闭包的定义。

  有很多不同的人都对闭包过进行了定义,这里收集了一些。

  是引用了自由变量的函数。这个函数通常被定义在另一个外部函数中,并且引用了

外部函数中的变量。 -- <<wikipedia>>

  是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。--

<<Java编程思想>>

  是一个匿名的代码块,可以接受参数,并返回一个返回值,也可以引用和使用在它

周围的,可见域中定义的变量。-- Groovy ['ɡru:vi]

  是一个表达式,它具有自由变量及邦定这些变量的上下文环境。

  闭包允许你将一些行为封装,将它像一个对象一样传来递去,而且它依然能够访问

到原来第一次声明时的上下文。

  是指拥有多个变量和绑定了这些变量的环境的表达式(通常是一个函数),因而这

些变量也是该表达式的一部分。

  闭包是可以包含自由(未绑定)变量的代码块;这些变量不是在这个代码块或者任

何全局上下文中定义的,而是在定义代码块的环境中定义。

  在这些定义中都有一些关键字:变量、函数、上下文等,闭包在回调函数、函数式

编程、Lambda表达式中有重要的应用,为了更深刻的理解闭包,我们会试图通过

JavaScript、C#和JAVA的代码进行举例,不过本次的重点还是通过JAVA如何这内部类来

实现闭包,以及闭包的应用。



二、JavaScript中的闭包。
  在JavaScript中,闭包是通过函数的嵌套来实现,以下是一个简单的例子:

<script type="text/javascript">
            function f1() {
                var n = 99;
                function f2() {
                    alert(n);
                }
                return f2();
            }
            f1();
</script>

这段代码的特点:

1、函数f1()还回了函数f2()

2、函数f2()引用了f1()定义的局变量

正常来讲,我们在外部是不能操作到f1()函数内部所定义的局部变量n,但是通过变通

的方法,我们在f1()函数内部定义了一个新的函数f2(),通过f2()输出其外围函数的局

部变量n,f2()是f1()的内部函数,对于f2()来说其外围函数所定义的变量、函数等上

下文是可以被内部函数所访问到的;最后在f1()函数中再调用f2()以在f1()被调用时触

发对f2()的调用,从而把局部变量输出。 我们对照一下闭包的定义:"引用了自由变量

的函数",这里的n就是定义中的自由变量,而函数f2()通过邦定自由变量n从而形式了

一个闭包。



三、JAVA中的闭包。

  在JAVA中,闭包是通过“接口+内部类”实现,像C#的delegate一样,JAVA的内部

类也可以有匿名内部类。我们现在就来详细认识一下JAVA内部类。

1、内部类。

  顾名思义,内部类就是将一个类定义在另一个类的内部。在JAVA中,内部类可以访

问到外围类的变量、方法或者其它内部类等所有成员,即使它被定义成private了,但

是外部类不能访问内部类中的变量。这样通过内部类就可以提供一种代码隐藏和代码组

织的机制,并且这些被组织的代码段还可以自由的访问到包含该内部类的外围上下文环

境。

这里提供了一个例子展示这种机制:
public class DemoClass1 {
    private int length =0;

    //private|public
    private class InnerClass implements ILog
    {
        @Override
        public void Write(String message) {
            //DemoClass1.this.length = message.length();
            length = message.length();
            System.out.println("DemoClass1.InnerClass:" + length);
        }
    }
   
    public ILog logger() {
        return new InnerClass();
    }

    public static void main(String[] args){
        DemoClass1 demoClass1 = new DemoClass1();
        demoClass1.logger().Write("abc");
       
        //.new
        DemoClass1 dc1 = new DemoClass1();
        InnerClass ic = dc1.new InnerClass();
        ic.Write("abcde");
    }
}

该例子的主要功能是实现一个写日志的ILog接口,但是该接口的类被定义在DemoClass1

这个外围类中了,而且这个InnerClass内部类还可以访问其外围类中的私有变量length



1.1、.new
  从上面的例子可见,InnerClass是定义在DemoClass1内部的一个内部类,而且

InnerClass还可以是Private。

如何创建这个InnerClass的实例? 可以通过外围类的实例进行创建,如:

DemoClass1 dc1 = new DemoClass1();
InnerClass ic = dc1.new InnerClass();
ic.Write("abcde");

1.2、.this
  如何通过this显式引用外围类的变量?通过此格式进行引用:{外围类名}.this.{

变量名称}。如:

  DemoClass1.this.length = message.length();

2、局部内部类。
  局部内部类是指在方法的作用域内定义的的内部类。

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类在方法的外围是不

可见的。

3、匿名内部类。
  顾名思义,匿名内部类就是匿名、没有名字的内部类,通过匿名内部类可以更加简

洁的创建一个内部类。

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...){}

4、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 {

5、实例初始化。

  匿名类的实例初始化相当于构造器的作用,但不能重载。

  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);
            }
        };
    }

匿名内部类的实例初始化工作可以通过符号 {...} 来标记,可以在匿名内部类实例化

时进行一些初始化的工作,但是因为匿名内部类没有名称,所以不能进行重载,如果必

须进行重载,只能定义成命名的内部类。


四、为什么需要闭包。

  闭包的价值在于可以作为函数对象或者匿名函数,持有上下文数据,作为第一级对

象进行传递和保存。闭包广泛用于回调函数、函数式编程中。

原生java没有提供Lambda表达式,不过可以使用尝试使用Scala的Lambda:

例子1:这个是闭包不?
scala> var add = (x: Int) => x +1
scala> add(10)

例子2:
scala> var more = 1
scala> var addMore = (x: Int) => x + more
scala> addMore(10)

五、闭包的问题。

1、让某些对象的生命周期加长。

  让自由变量的生命周期变长,延长至回调函数执行完毕。

2、闭包共享。

  inal关键字

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<Action> ,先向列表中创建 i 个匿名内部类new

Action(),然后通过for遍历读出。

因为 i 变量在各个匿名内部类中使用,这里产生了闭包共享,java编译器会强制要求

传入匿名内部类中的变量添加final

关键字,所以这里final int copy = i;需要做一个内存拷贝,否则编译不过。(在c#中

没有强制要求会导致列有被遍历时

始终会取 i 最大值,这是因为延迟执行引起的)

参考网址:
http://www.cnblogs.com/chenjunbiao/archive/2011/01/26/1944417.html
分享到:
评论

相关推荐

    Java闭包 Java闭包

    JSR-335 将闭包引入了 Java 。闭包在现在的很多流行的语言中都存在,例如 C++、C# 。闭包允许我 们创建函数指针,并把它们作为参数传递。在这篇文章中,将粗略的看一遍Java8的特性,并介绍 Lambda表达式。而且将试...

    详解Java的闭包

    主要介绍了详解Java的闭包,作者从Lambda和默认方法等重要特性深入讲解,极力推荐!需要的朋友可以参考下

    Java闭包练习

    Java8 Java7 练手 类似于SessionTemplate

    闭包搜索算法java编程

    It is important in distributed computer systems to identify those events (at identifiable points in time) that are concurrent, or not related to each other in time. A group of concurrent events may ...

    Java中的闭包与回调

    闭包是可以包含自由(未绑定)变量的代码块;这些变量不是在这个代码块或者任何全局上下文中定义的,而是在定义代码块的环境中定义。“闭包”一词来源于以下两者的结合:要执行的代码块(由于自由变量的存在,相关...

    Recursive:Java 8 的递归闭包

    递归Java 8 的递归闭包基于stackoverflow上的这个答案: : 包含一个简单的用法演示。 查看我的另一个函数式编程项目: : 博客条目: :

    Java函数式编程(五):闭包

    主要介绍了Java函数式编程(五):闭包,本文是系列文章的第5篇,其它篇章请参阅相关文章,需要的朋友可以参考下

    Java中闭包简单代码示例

    主要介绍了Java中闭包简单代码示例,具有一定借鉴价值,需要的朋友可以参考下

    JavaSript中变量的作用域闭包的深入理解

    闭包:函数内部可以调用函数外部的变量;反之,则不行 */ var r=10; function test1(){ var r2=”abc”; //alert&#40;r&#41; } //alert&#40;r2&#41;;//不能访问的函数内部的r2 test1(); //嵌套的函数中也ok啦 function

    离散数学的各种闭包运算

    离散数学 闭包运算 传递闭包 自反闭包 对称闭包 warshell算法 普通算法 有界面 java编程

    传递闭包warshall算法java实现

    传递闭包的Warshall算法,java实现

    Java内部类之间的闭包和回调详解

    相信闭包和回调对每位学习Java的人来说都不陌生,那么今天小编和大家分享一篇关于Java内部类之间的闭包和回调,有需要的可以参考借鉴。

    java-lambdas-closures

    Java闭包和Lambda Java-8-Lambdas Java 9语言功能,第二版 而且因为我喜欢TDD,所以每一个练习都从使用JUnit4进行测试开始,在如此极端的水平上,有些练习比仅包含测试要多。 内容 Lambdas 目标键入功能接口方法...

    JavaScript中通过闭包解决只能取得包含函数中任何变量最后一个值的问题

    代码如下: [removed] function createArray() { var arr = new Array(); for (var i = 0; i ”); } //以上输出全部是i的最后一次的值(10),即

    山东大学三元闭包实验

    自己码的代码,R语言实现,

    跨越边界:闭包

    一些人认为闭包带给编程语言的...尽管一些颇具竞争力的语言(如 C#)采纳了闭包,但Java社区至今仍抵制对它的使用。本文探讨闭包在为编程语言带来一点点便利的同时是否也带来不必要的复杂性、闭包还有无更多的益处。

    firebase-js-sdk:Firebase Javascript SDK

    闭包编译器需要现代Java安装。 应该安装Java 8+: : 验证先决条件 您可以通过在终端中运行以下命令来验证设置: $ node -v $ yarn -v $ java -version 您的Node.js版本应为10.15.0或更高版本, yarn版本应为1.0.0...

    讲义13闭包高级技术.docx

    闭包包含自由(未绑定到特定对象)变量;这些变量不是在这个代码块内或者任何全局上下文中定义的,而是在定义代码块的环境中定义(局部变量)。"闭包" 一词来源于以下两者的结合:要执行的代码块(由于自由变量被包含在代码...

    什么是闭包?闭包的优缺点?

    什么是闭包?闭包的优缺点?

    android-linq:使用C#LINQ样式查询和Java 8闭包轻松操作集合

    Android LINQ 使用C#LINQ样式查询和Java 8闭包轻松操作集合。描述Android LINQ是受Microsoft C#LINQ库启发的集合操作实用程序的一小部分,面向希望使用新Java 8 Stream()API的Android开发人员。 通过将 ,开发...

Global site tag (gtag.js) - Google Analytics