函数式编程
函数式编程是一种编程方式,它将电脑运算视为函数的计算。函数编程语言最重要的基础是λ演算(lambda calculus),λ演算的函数可以接受函数当作输入(参数)和输出(返回值)。 函数式编程是"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。
lambda表达式
JSR-335首次定义了在Java中使用lambda表达式的基本规范,当前的实现就是针对JSR-335规范的。 lambda表达式是一种紧凑的、传递行为的方式。Lambda表达式本质上是为了解决方便的将代码作为数据传递的难题。从本质上讲lambda表达式就是一种将行为参数化的操作。
在JDK8之前,需要利用匿名内部类来实现的传递行为的操作,现在可以用lambda表达式来实现了。 从编码的角度讲,lambda表达式是一个代码块,以及必须传入代码的变量规范。
由于规范基本一致,所以Java中的lambda表达式和JavaScript中的在使用上几乎完全一样。所差别的只是胖箭头和瘦箭头而已。当然由于语言特性的问题,两种lambda表达式的实际差距还是很大的,这种差距表现在:在Java中,lambda表达式本身是有类型的。也就是说要传递的行为必须是有类型的,这种类型就是函数接口。
除了传参之外,lambda表达式也可以出现在赋值符号的右边。
lambda表达式的语法: lambda表达式由参数、箭头、表达式组成。
(parameters) -> expression //此处行为为表达式,这里隐含了return语句。
或者
(parameters) -> { statements; } //此处行为为语句,需要返回时要手写return语句。
1,如果代码无法在一个表达式中完成,可以像写方法一样把代码放在大括号中。只有一行代码的Lambda表达式也可以使用大括号。
()->{
...
}
2,没有参数时,也要提供一个空的小括号。如果只有一个参数,而且这个参数的类型可以推导,也可以省略小括号。Lambda表达式也可以包含多个参数
(param)->expression
param->expression
3,大部分情况中,无需指定lambda表达式的返回类型。具体的返回类型可以由上下文推断得出。
这种类型推导实际上是Java7中的目标类型推断的扩展。在Java7中以下代码是合法的,这是泛型的又一个升级。
Map map=new HashMap<>(); //不用声明HashMap的中的键值对类型,系统可以通过Map的相关类型推断出来。
这就是所谓的类型推断。
4,lambda表达式中只在某些分支返回值,另一个些分支不返回值是不合法的。
5,与JavaScript相同,lambda表达式只有一个入参时,可以省略小括号。
example:
BinaryOperator add=(x,y)->x+y; //创建一个函数,用来计算x和y相加的结果。
注意:add不是两个数字的和,而是将两个数字加相的那行代码。
System.out.println(add.apply(1l,2l)); //打印3
解析:binaryOperator是一个接口,其含义进行一次两数据的某种操作,并返回这个操作的结果。其抽象方法来自父接口BiFunction。BiFunction接口是不同数据类型的操作、而binaryOperator接口是针对相同类型数据的操作。
public interface BiFunction {
R apply(T t, U u);
}
public interface BinaryOperator extends BiFunction{}
需要注意的是: Lambda表达式中引用的在表达式之外定义的局部变量必须是final或既成事实上的final变量。 这个约束性的规定来源于java 8之前的匿名内部类,java 8放宽了这个规定,既局部变量可以不加final关键字,但依然不能给该变量多次赋值。
函数接口
函数接口是只有一个抽象方法的接口,用作Lambda表达式的类型。也就是说所谓的Lambda表达式对应的其实就是函数接口。 java8提供@FunctionalInterface注解函数接口,当然这个注解是非必须的,只要接口符合函数接口的标准(即只包含一个抽象方法的接口)即可。 函数式接口的抽象方法签名基本上就是Lambda表达式的签名,这种抽象方法叫作函数描述符。 几个重要的函数式接口,它们都位于java.util.function包中:
Predicate接口
@FunctionalInterface
public interface Predicate{
boolean test(T t);
}
Consumer接口
@FunctionalInterface
public interface Consumer{
void accept(T t);
}
BiConsumer接口
@FunctionalInterface
public interface BiConsumer {
void accept(T t, U u);
}
Function接口
@FunctionalInterfac
public interface Function{
R apply(T t);
}
为避免在使用原始类型时进行自动拆、装箱操作,jdk8提供了以上函数式接口的原始类型版本。
IntPredicate、DoublePredicate等
Supplier接口
@FunctionalInterface
public interface Supplier {
T get();
}
BinaryOperator接口
BinaryOprator接口继承自BiFunction接口,其接口方法源型如下:
R apply(T t, U u);
方法引用
方法引用是lambda表达式的一种简化写法。当要调用的方法是一个已经存在的方法时可以使用方法引用。使用方法引用替换整个Lambda表达式。 语法为:左边是容器(可以是类名,实例名),中间是"::",右边是相应的方法名(不带小括号)。
ObjectReference::methodName
一般方法的引用格式:
调用静态方法,ClassName::methodName。如 Person::compareByAge调用实例方法,Instance::methodName。如System.out::println;调用参数的实例方法,ClassName::methodName。Object::equals;调用构造方法,ClassName::new 。如:Person::new;注意:
当匿名方法有参数并且lambda表达式就是调用参数中的方法的时候,可以直接用参数的类名做为容器。如下:
List collected=Stream.of("a","b","hello")
.map(string->string.toUpperCase)
.collect(Collectors.toList());
//注意这里使用了方法引用
List collected=Stream.of("a","b","hello")
.map(String::toUpperCase)
.collect(Collectors.toList());
本质上讲:方法引用是Lambda表达式的快捷写法。当这个Lambda表达式代表的只是直接调用某个已经存在的方法时使用。
可以把方法引用当作针对仅涉及单一方法的Lambda的语法糖。
forExample:
public class LambdaExample {
public static void main(String[] args) {
List names = Arrays.asList("peter", "anna", "mike", "xenia");
/*匿名内部类的方式
Collections.sort(names, new Comparator() {
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
*/
/*lambda表达式
Collections.sort(names, (a,b) -> {
return b.compareTo(a);
});
*/
//简易写法
Collections.sort(names, (a, b) -> b.compareTo(a));
//进一步简写
Collections.sort(names,String::compareTo);
for(String s:names){
System.out.println(s);
}
}
}
public class ThreadExample {
public static void main(String[] args) {
Thread thread=new Thread(()->{
for(int i=0;i<10;i++){
System.out.println(i);
}
});
thread.start();
}
}
每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为默认方法不算抽象方法,所以你也可以给你的函数式接口添加默认方法。 我们可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。
@FunctionalInterface
interface Converter {
T convert(F from);
}
Converter converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted); // 123
下一篇lambda2讲解具体的API语法,以及应用场景。
Java8中lambda表达式的语法,别人都会的,你还不会吗?「二」