在编写程序的时候,当你的 jdk 为8 时,你会看到很多类似这样的代码
bList.stream().reduce((a,b) -> a.add(b));
如果你之前没有接触过 Java8 的新特性,肯定会对这个有疑惑,看不懂。所以最简单的需求就是:看懂代码,然后学会新特性,让自己也能写出类似上面这样简洁的代码。
通过网上的查询,了解,通过自己的实践敲代码,然后就是记下这篇的感悟。
首先我们要知道这样的写法是来自于 Java8 的新特性:lambda(函数式编程)
在 Java8 中,lambda 是依赖于函数式接口的,函数式接口是什么呢?
函数式接口:简单来说就是一个接口 (interface)只有一个方法。不能有多余的方法,当然默认方法除开,比如下面:
interface PersonInterface{
int a(int c,int b);
default void a(){
System.out.println("hello");
}
}
如果有一个方法需要用到这个接口当中参数,比如如下:
public static void test(PersonInterface c){
int a = 5;
int b= 2;
System.out.println(c.a(a,b));
}
我们常见的方式就是:
test(new PersonInterface() {
@Override
public int a(int c, int b) {
return c+b;
}
});//通过匿名类的方式实现 PersonInterface 接口。
但是现在如果你的 jdk 是 8 ,那么你可以尝试这样写:
test((a,b) -> a+b);
它们的结果是一致的。
通过上面简单的一个例子,让我们知道了 lambda 的使用,下面就先熟息下 lambda 的语法。
Lambda 的语法
三部分:
参数:可以有参数,有时又没有,主要还是看方法是否需要参数,跟方法对应的参数个数一致。
无参数的时候:() -> { expression }
单参数的时候:a -> { expression }
多参数的时候:(a,b .....) -> { expression }
符号:->
方法体:表达式或者代码段,当方法有返回值时就需要 return (单行代码时,默认有 return 的),如果是 void 那就不需要。当代码行多时,就用 {} 括起来,单行的话,不需要。
下面多看几个例子:
List<Person> pList = Arrays.asList(new Person(18,"美呀"), new Person(6,"小新"), new Person(1,"小葵"));
pList.stream().forEach(new Consumer<Person>() {
@Override
public void accept(Person person) {
System.out.println(person.getName());
}
});
pList.stream().forEach(p -> System.out.println(p.getName()));
List<BigDecimal> bList = Arrays.asList(new BigDecimal(18), new BigDecimal(26), new BigDecimal(23));
System.out.println(bList.stream().reduce(new BinaryOperator<BigDecimal>() {
@Override
public BigDecimal apply(BigDecimal bigDecimal, BigDecimal bigDecimal2) {
return bigDecimal.add(bigDecimal2);
}
}));
System.out.println(bList.stream().reduce((a,b) -> a.add(b)));
stream.reduce 等等这些写法,可以暂时不去纠结,它也是 java8 新特性 流(stream)。我们重点看 lambda 的写法。
通过上面的例子我们可以看出第一点:那就是 lambda 的方法体其实就是函数式接口中方法的实现:
a.add(b) == bigDecimal.add(bigDecimal2) p.getName() == person.getName()
第二点:lambda 的参数,其实就是函数式接口中方法的参数
apply(BigDecimal bigDecimal, BigDecimal bigDecimal2) == (a,b) accept(Person person) == p
综上两点,可以看出 lamdba 其实就是对函数式接口的实现(有点像匿名类,但是代码有简洁太多啦),前面是函数式接口中方法的参数,后面就是方法中的内容。
到这里:我们就已经可以看懂这些代码,同时也会写啦。首先,需要函数式接口(一个接口只能有一个方法,为了防止很多人把它当成普通接口,可以在接口名上面加一个注解 @FunctionalInterface,它会为你检测的,当你想加入第二个方法的时候,会报错的),然后就是开始 lambda 的写法啦。
还没有完,当我们写代码的时候会发现,方法里面有很多内容,这个时候发现 lambda 写法代码也开始变多啦。所以引出了一个更简化的写法:方法引用。
方法引用格式:
(ClassName :: methodName) 、(ClassName :: new) 这个是调用构造函数
例子:
bList.stream().mapToDouble(BigDecimal :: doubleValue).reduce(Double :: sum)
bList.stream().mapToDouble(new ToDoubleFunction<BigDecimal>() {
@Override
public double applyAsDouble(BigDecimal value) {
return value.doubleValue();
}
}).reduce(Double :: sum)
bList.stream().mapToDouble(BigDecimal :: doubleValue).reduce(new DoubleBinaryOperator() {
@Override
public double applyAsDouble(double left, double right) {
return left + right;
}
})
从上面的例子可以看出,BigDecimal :: doubleValue value.doubleValue(); 其实就是让参数调用 doubleValue 方法,Double :: sum left + right 也是不断的让 集合中的 值相加,其实也就是 sum 加总。
至此:lamdba 在 Java8 中的写法和运用都写在上面了,第一个就是函数式接口,熟息 lambda 语法,第二就是方法引用。学会这个后,后面就要去了解 stream 流,因为 lambda 如果不和 stream 结合起来,你会发现用到的地方不是很多。而 stream 会在下篇介绍。
目前理解只有这样,如果有人发现问题,欢迎指出,相互交流。