第一章.java&golang的区别之:闭包
- java8之前的闭包
1 public class ClosureBeforeJava8 { 2 int y = 1; 3 4 public static void main(String[] args) { 5 final int x = 0; 6 ClosureBeforeJava8 closureBeforeJava8 = new ClosureBeforeJava8(); 7 Runnable run = closureBeforeJava8.getRunnable(); 8 new Thread(run).start(); 9 } 10 11 public Runnable getRunnable() { 12 final int x = 0; 13 Runnable run = new Runnable() { 14 @Override 15 public void run() { 16
17 System.out.println("local varable x is:" + x); 18 //System.out.println("member varable y is:" + this.y); //error 19 } 20 }; 21 return run; 22 } 23 }
上段代码的输出:local varable x is:0
在代码的第13行到第20行,通过匿名类的方式实现了Runnable接口的run()方法,实现了一部分操作的集合(run方法),并将这些操作映射为java的对象,在java中就可以实现将函数以变量的方式进行传递了,如果仅仅是传递函数指针,那还不能算是闭包,我们再注意第17行代码,在这段被封装可以在不同的java对象间传递的代码,引用了上层方法的局部变量,这个就有些闭包的意思在里面了。但是第18行被注释掉的代码在匿名类的情况下却无法编译通过,也就是封装的函数里面,无法引用上层方法所在对象的成员变量。总结一下,java8之前的闭包特点如下:
1.可以实现封装的函数在jvm里进行传递,可以在不同的对象里进行调用;
2.被封装的函数,可以调用上层的方法里的局部变量,但是此局部变量必须为final,也就是不可以更改的(基础类型不可以更改,引用类型不可以变更地址);
- java8里对闭包的支持
java8里对于闭包的支持,其实也就是lamda表达式,我们再来看一下上段代码在lamda表达式方式下的写法:
1 public class ClosureInJava8 { 2 int y = 1; 3 4 public static void main(String[] args) throws Exception{ 5 final int x = 0; 6 ClosureInJava8 closureInJava8 = new ClosureInJava8(); 7 Runnable run = closureInJava8.getRunnable(); 8 Thread thread1 = new Thread(run); 9 thread1.start(); 10 thread1.join(); 11 new Thread(run).start(); 12 } 13 14 public Runnable getRunnable() { 15 final int x = 0; 16 Runnable run = () -> { 17
18 System.out.println("local varable x is:" + x); 19 System.out.println("member varable y is:" + this.y++); 20 }; 21 return run; 22 } 23 }
上面对代码输出:
local varable x is:0
member varable y is:1
local varable x is:0
member varable y is:2
在代码的第16行到第20行,通过lamda表达式的方式实现了函数的封装(关于lamda表达式的用法,大家可以自行google)。通过代码的输出,大家可以发现,在lamda表达式的书写方式下,封装函数不但可以引用上层方法的effectively final类型(java8的特性之一,其实也是final类型)的局部变量,还可以引用上层方法所在对象的成员变量,并可以在其它线程和方法中对此成员变量进行修改。总结一下:java8对于闭包支持的特点如下:
1.通过lamda表达式的方式可以实现函数的封装,并可以在jvm里进行传递;
2.lamda表达式,可以调用上层的方法里的局部变量,但是此局部变量必须为final或者是effectively final,也就是不可以更改的(基础类型不可以更改,引用类型不可以变更地址);
- golang里对闭包的支持
golang里对于闭包的支持,理解起来就非常容易了,就是函数可以作为变量来传递使用,代码如下:
1 package main 2 3 import "fmt" 4 5 func main() { 6 ch := make(chan int ,1) 7 ch2 := make(chan int ,1) 8 fn := closureGet() 9 go func() { 10 fn() 11 ch <-1 12 }() 13 go func() { 14 fn() 15 ch2 <-1 16 }() 17 <-ch 18 <-ch2 19 } 20 21 func closureGet() func(){ 22 x := 1 23 y := 2 24 fn := func(){ 25 x = x +y 26 fmt.Printf("local varable x is:%d y is:%d \n", x, y) 27 } 28 return fn 29 }
代码输出如下:
local varable x is:3 y is:2
local varable x is:5 y is:2
代码的第24行到27行,定义了一个方法fn,此方法可以使用上层方法的局部变量,总结一下:
1.golang的闭包在表达形式上,理解起来非常容易,就是函数可以作为变量,来直接传递;
2.golang的封装函数可以没有限制的使用上层函数里的局部变量,并且在不同的goroutine里修改的值,都会有所体现。
关于第2点,大家可以参考文章:https://studygolang.com/articles/11627 中关于golang闭包的讲解部分。
- 总结
golang的闭包从语言的简洁性、理解的难易程度、支持的力度上来说,确实还是优于java的。本文作为java和golang对比分析的第一篇文章,由于调研分析的时间有限,难免有疏忽之处,欢迎各位指正。