Java函数式编程(待续)
函数式编程概念
面向对象抽象数据,函数式编程抽象行为。---摘自《on java8》
下面是廖雪峰老师在Python教程中对函数式编程的一段解释:
而函数式编程(请注意“式”字)——Functional Programming,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。
我们首先要搞明白计算机(Computer)和计算(Compute)的概念。在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。
而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。
函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!
所以函数式编程又如下特点:
- 没有变量改变
- 接受函数当作输入(参数)和输出(返回值)
- 一个输入对应一个输出
- 缓求值,提高效率
- 必报
以上是我对函数式编程的一些理解。
Java引入函数式编程
在java8之前,我们要想一个方法的行为随着输入的改变而改变(其他条件一致),就要出入一个封装又相应行为的对象。通过这个对象的行为改变方法的行为。
java8之后,我们可以通过Lambda表达式传入对应的行为,简化代码。为了简化Lambda表达式,我们可以利用方法引用来将一个方法当作Lambda表达式传入进方法。
Lambda表达式和方法引用语法
语法具体参看《on java8》
在说Lambda表达式之前,先参考细说匿名内部类引用方法局部变量时为什么需要声明为final这篇博文了解一下匿名内部类对外部变量的处理。
以下式个人理解:
下面是Lambda表达式的demo以及反编译信息
package functional;
interface WithOneArgs {
String detailed(String head);
}
public class MyLambdaExpressions {
private static final String CONSTANT_STRING = "constant string!";
private String INSTANCE_STRING = "instance string!";
private static WithOneArgs staticLambda = h -> " constant string!";
private static WithOneArgs staticLambda2 = h -> h + CONSTANT_STRING;
private WithOneArgs instanceLambda = h -> h + h +" constant string!";
private WithOneArgs instanceLambda2 = h -> h + h + h + INSTANCE_STRING;
public void lambdaMethod(){
String test = "local string!";
WithOneArgs localLambda = h -> h + h + h + h+ test;
WithOneArgs localLambda1 = h -> h + h + h + h + h+ "local string!";
WithOneArgs localLambda2= h -> h + h + h + h + h + h+CONSTANT_STRING;
System.out.println(localLambda.detailed("staticLambda!"));
System.out.println(localLambda1.detailed("staticLambda!"));
System.out.println(localLambda2.detailed("staticLambda!"));
}
public static void main(String[] args) {
System.out.println(staticLambda.detailed("staticLambda!"));
System.out.println(staticLambda2.detailed("staticLambda2!"));
MyLambdaExpressions myLambdaExpressions = new MyLambdaExpressions();
System.out.println(myLambdaExpressions.instanceLambda.detailed("instanceLambda!"));
System.out.println(myLambdaExpressions.instanceLambda2.detailed("instanceLambda2!"));
myLambdaExpressions.lambdaMethod();
}
}
public class functional.MyLambdaExpressions
{
private static final java.lang.String CONSTANT_STRING;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
ConstantValue: String constant string!
private java.lang.String INSTANCE_STRING;
descriptor: Ljava/lang/String;
flags: ACC_PRIVATE
private static functional.WithOneArgs staticLambda;
descriptor: Lfunctional/WithOneArgs;
flags: ACC_PRIVATE, ACC_STATIC
private static functional.WithOneArgs staticLambda2;
descriptor: Lfunctional/WithOneArgs;
flags: ACC_PRIVATE, ACC_STATIC
private functional.WithOneArgs instanceLambda;
descriptor: Lfunctional/WithOneArgs;
flags: ACC_PRIVATE
private functional.WithOneArgs instanceLambda2;
descriptor: Lfunctional/WithOneArgs;
flags: ACC_PRIVATE
public functional.MyLambdaExpressions();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=2, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #2 // String instance string!
7: putfield #3 // Field INSTANCE_STRING:Ljava/lang/String;
10: aload_0
11: invokedynamic #4, 0 // InvokeDynamic #0:detailed:()Lfunctional/WithOneArgs;
16: putfield #5 // Field instanceLambda:Lfunctional/WithOneArgs;
19: aload_0
20: aload_0
21: invokedynamic #6, 0 // InvokeDynamic #1:detailed:(Lfunctional/MyLambdaExpressions;)Lfunctional/WithOneArgs;
26: putfield #7 // Field instanceLambda2:Lfunctional/WithOneArgs;
29: return
LineNumberTable:
line 5: 0
line 7: 4
line 10: 10
line 11: 19
public void lambdaMethod();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=3, locals=5, args_size=1
0: ldc #8 // String local string!
2: astore_1
3: aload_1
4: invokedynamic #9, 0 // InvokeDynamic #2:detailed:(Ljava/lang/String;)Lfunctional/WithOneArgs;
9: astore_2
10: invokedynamic #10, 0 // InvokeDynamic #3:detailed:()Lfunctional/WithOneArgs;
15: astore_3
16: invokedynamic #11, 0 // InvokeDynamic #4:detailed:()Lfunctional/WithOneArgs;
21: astore 4
23: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: ldc #13 // String staticLambda!
29: invokeinterface #14, 2 // InterfaceMethod functional/WithOneArgs.detailed:(Ljava/lang/String;)Ljava/lang/String;
34: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
37: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
40: aload_3
41: ldc #13 // String staticLambda!
43: invokeinterface #14, 2 // InterfaceMethod functional/WithOneArgs.detailed:(Ljava/lang/String;)Ljava/lang/String;
48: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
51: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
54: aload 4
56: ldc #13 // String staticLambda!
58: invokeinterface #14, 2 // InterfaceMethod functional/WithOneArgs.detailed:(Ljava/lang/String;)Ljava/lang/String;
63: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
66: return
LineNumberTable:
line 13: 0
line 14: 3
line 15: 10
line 16: 16
line 17: 23
line 18: 37
line 19: 51
line 21: 66
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #16 // Field staticLambda:Lfunctional/WithOneArgs;
6: ldc #13 // String staticLambda!
8: invokeinterface #14, 2 // InterfaceMethod functional/WithOneArgs.detailed:(Ljava/lang/String;)Ljava/lang/String;
13: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
16: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
19: getstatic #17 // Field staticLambda2:Lfunctional/WithOneArgs;
22: ldc #18 // String staticLambda2!
24: invokeinterface #14, 2 // InterfaceMethod functional/WithOneArgs.detailed:(Ljava/lang/String;)Ljava/lang/String;
29: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
32: new #19 // class functional/MyLambdaExpressions
35: dup
36: invokespecial #20 // Method "<init>":()V
39: astore_1
40: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
43: aload_1
44: getfield #5 // Field instanceLambda:Lfunctional/WithOneArgs;
47: ldc #21 // String instanceLambda!
49: invokeinterface #14, 2 // InterfaceMethod functional/WithOneArgs.detailed:(Ljava/lang/String;)Ljava/lang/String;
54: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
57: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;
60: aload_1
61: getfield #7 // Field instanceLambda2:Lfunctional/WithOneArgs;
64: ldc #22 // String instanceLambda2!
66: invokeinterface #14, 2 // InterfaceMethod functional/WithOneArgs.detailed:(Ljava/lang/String;)Ljava/lang/String;
71: invokevirtual #15 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
74: aload_1
75: invokevirtual #23 // Method lambdaMethod:()V
78: return
LineNumberTable:
line 23: 0
line 24: 16
line 25: 32
line 26: 40
line 27: 57
line 28: 74
line 29: 78
private static java.lang.String lambda$lambdaMethod$6(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: new #24 // class java/lang/StringBuilder
3: dup
4: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
7: aload_0
8: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_0
12: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_0
16: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: aload_0
20: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: aload_0
24: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: aload_0
28: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
31: ldc #27 // String constant string!
33: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: invokevirtual #28 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
39: areturn
LineNumberTable:
line 16: 0
private static java.lang.String lambda$lambdaMethod$5(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: new #24 // class java/lang/StringBuilder
3: dup
4: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
7: aload_0
8: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_0
12: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_0
16: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: aload_0
20: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: aload_0
24: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: ldc #8 // String local string!
29: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32: invokevirtual #28 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
35: areturn
LineNumberTable:
line 15: 0
private static java.lang.String lambda$lambdaMethod$4(java.lang.String, java.lang.String);
descriptor: (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: new #24 // class java/lang/StringBuilder
3: dup
4: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
7: aload_1
8: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_1
12: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: aload_1
20: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: aload_0
24: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: invokevirtual #28 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: areturn
LineNumberTable:
line 14: 0
private java.lang.String lambda$new$3(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PRIVATE, ACC_SYNTHETIC
Code:
stack=2, locals=2, args_size=2
0: new #24 // class java/lang/StringBuilder
3: dup
4: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
7: aload_1
8: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_1
12: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1
16: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: aload_0
20: getfield #3 // Field INSTANCE_STRING:Ljava/lang/String;
23: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
26: invokevirtual #28 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
29: areturn
LineNumberTable:
line 11: 0
private static java.lang.String lambda$new$2(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: new #24 // class java/lang/StringBuilder
3: dup
4: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
7: aload_0
8: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: aload_0
12: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: ldc #29 // String constant string!
17: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: invokevirtual #28 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
23: areturn
LineNumberTable:
line 10: 0
private static java.lang.String lambda$static$1(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: new #24 // class java/lang/StringBuilder
3: dup
4: invokespecial #25 // Method java/lang/StringBuilder."<init>":()V
7: aload_0
8: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
11: ldc #27 // String constant string!
13: invokevirtual #26 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: invokevirtual #28 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
19: areturn
LineNumberTable:
line 9: 0
private static java.lang.String lambda$static$0(java.lang.String);
descriptor: (Ljava/lang/String;)Ljava/lang/String;
flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: ldc #29 // String constant string!
2: areturn
LineNumberTable:
line 8: 0
static {};
descriptor: ()V
flags: ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: invokedynamic #30, 0 // InvokeDynamic #5:detailed:()Lfunctional/WithOneArgs;
5: putstatic #16 // Field staticLambda:Lfunctional/WithOneArgs;
8: invokedynamic #31, 0 // InvokeDynamic #6:detailed:()Lfunctional/WithOneArgs;
13: putstatic #17 // Field staticLambda2:Lfunctional/WithOneArgs;
16: return
LineNumberTable:
line 8: 0
line 9: 8
}
根据是上面的反编译代码可以看到,编译器把每个Lambda表达式编译成了一个静态方法,名字是lambda${static|new|lambdaMethod...}${seq}。第一个单词lambda应该是固定的,表名是lambda表达式;第二个单词取决于lambda声名的位置和引用的变量,如果lambda表达式是类的域,且没有涉及到类变量,那么就是static,否则是new,如果是方法内部,是lambdaMethod,目前实验的是这些。可以看到上面例子中,第4个Lambda表达式用到了实例变量,所以生成的是一个实例方法,private java.lang.String lambda3(java.lang.String)。seq是lambda表达式在程序中声名的序号,从零开始。
但是当我们使用表达式的时候,我们使用的是接口方法,那么我们调用接口方法的时候,又是怎么调用对应的lambda表达式的?这里我们不得不提到java8为lambda表达式新增加的一个指令了,Invokedynamic 。
具体调用方式可以参考:Java 8 动态类型语言Lambda表达式实现原理分析
方法引用
java8可以将一个具有相同签名的类的方法当作lambda表达式赋值给一个函数式接口。为了方便引用已经存在的方法,Java8引用了一个特殊的语法:”::”。有两种引用方法,类名::方法名,实例变量名::方法名。如果是一个静态方法,那么使用类名::方法名引用即可。但是如果是一个实例方法,可以使用两种的任一种,如果使用实例变量名::方法名,需要实现实例化这个类的对象,然后使用实例变量名::方法名将方法引用复制给接口声名的变量;使用类名::方法名的时候,可以直接使用类名::方法名将方法引用复制给接口声名的变量,但是对函数式接口有特殊要求,这个函数式接口的函数的参数必须要有一个参数是这个类的类型,在调用这个函数式接口的时候要将这个类的一个实例变量传递给这个函数式接口方法。
高级特性
产生和消耗函数。其实就是方法内部产生/使用一个函数式接口对象。
闭包:
参考:
《on java8》
《jvms8》jvm8规范