`

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闭包是一个重要的编程概念,尤其在Java 8及以后的版本中得到了广泛的应用。闭包是一种函数式编程特性,允许函数保留对外部环境的引用,即使该函数被作为独立实体传递或执行。在Java中,接口中的默认方法和Lambda...

    详解Java的闭包

    Java 闭包是一种强大的编程工具,它允许程序员创建可以捕获和存储其周围环境状态的函数。在 Java 8 及以后的版本中,闭包的实现主要通过 Lambda 表达式和默认方法来体现。 Lambda 表达式是 Java 8 引入的一项重要...

    闭包搜索算法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 ...

    传递闭包warshall算法java实现

    传递闭包是图论中的一个重要概念,特别是在研究有向图的可达性问题时。Warshall算法是由美国计算机科学家Stephen Warshall于1962年提出的,它主要用于计算有向图的所有节点对之间的传递闭包。在图中,如果存在一条从...

    Recursive:Java 8 的递归闭包

    在Java 8中,递归和闭包是两个重要的概念,它们在函数式编程中扮演着核心角色。本文将深入探讨这两个概念,并结合实际示例解释它们如何在Java 8中协同工作。 首先,让我们理解“递归”。递归是一种算法,其中函数或...

    Java中的闭包与回调

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

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

    Java函数式编程中的闭包是一种强大的工具,它允许函数访问并操作其定义时的作用域内的变量,即使该函数被传递到其他上下文中。在描述的示例中,我们看到闭包是如何帮助消除代码冗余的。 当我们需要创建多个类似的...

    Java中闭包简单代码示例

    Java中闭包简单代码示例 在 Java 中,闭包是一个非常重要的概念,它允许开发者在函数中捕捉和保存变量的值,从而实现代码的复用和模块化。在本文中,我们将通过一个简单的示例代码,来了解 Java 中闭包的定义和应用...

    Java中的闭包与回调.rar

    Java中的闭包与回调是编程领域中的两个重要概念,尤其在Java这门广泛使用的编程语言中,它们在函数式编程和异步处理中扮演着关键角色。本文将深入探讨这两个概念,以及它们如何在实际开发中应用。 首先,让我们来...

    离散数学的各种闭包运算

    7. **有界面的Java编程**:在实际应用中,我们可能会编写Java程序来实现这些闭包运算,并提供用户界面进行交互。这可能涉及到图形用户界面(GUI)的开发,如Swing或JavaFX,用户可以输入关系矩阵,程序计算并显示...

    山东大学三元闭包实验

    在IT领域,尤其是在计算机科学和数据处理中,"三元闭包"是一个重要的概念,尤其在图论、数据库理论和程序设计中有着广泛的应用。这个概念源于数学,但在编程语言中,它通常与函数和作用域相关。在这个“山东大学三元...

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

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

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

    闭包和回调是Java编程中的两个重要概念,它们在处理函数式编程和事件驱动编程时起着关键作用。 首先,让我们来理解闭包。在Java中,闭包是一种能够访问和修改其封闭作用域(创建它的环境)变量的能力的函数或代码块...

    closure闭包

    在Java中,你可以使用Lambda表达式或匿名内部类来创建闭包,而在JavaScript中,所有的函数都是闭包。在C#中,可以使用`Action`、`Func`等委托类型或`lambda`表达式来创建闭包。 总的来说,闭包在编程中扮演着至关...

    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),即

    传递闭包实现

    根据给定文件的信息,我们可以深入探讨“传递闭包”的概念及其在计算机科学中的实现方法。 ### 传递闭包的概念 传递闭包(Transitive Closure)是集合论中的一个概念,指的是在集合\(X\)上的一类特殊的二元关系。...

    跨越边界:闭包

    然而,对于某些社区,如Java,闭包的引入引发了一些争议。一些开发者认为闭包增加了语言的复杂性,破坏了简洁性,而另一些人则相信它们能带来新的设计模式和效率提升。 闭包的核心在于它可以捕获和存储其定义时的...

Global site tag (gtag.js) - Google Analytics