目录
导言
起源
尝试一下
什么是Lambda表达式
这几天貌似互联网动作很大,阿里巴巴投资ofo,美团打车要上线,阿里收购饿了么,美团收购摩拜...而朋友圈,更是被一篇鸡血文刷屏,大意是80后的摩拜创始人在公司收购后拿到15,相较之下,而同龄人都是失败者,甚至不配活在这个世上了...
就在想,80尾巴出生的我,是什么生活呢?按照这个逻辑,无疑是loser,也即将面临中年危机,那怎么办?
好好学好java吧!升职加薪,晚饭加鸡腿比较实际点,如果还被这种文章鼓动,要想发财,要想暴富,马上辞职,背上包包,告别家人,奔赴北上深杭,估计多半会春夏秋冬,造福房东!
Lambda表达式是JAVA8引进,可以称之为函数式编程。这在其它语言很常见,比如javascript;但在JAVA中却是一个很大的改动。那什么是函数式编程呢?根本不重要,我们需要的更简单的api,以更容易写出稳定的代码,理解的集合操作,提高计算机的利用率...
只做一点简单的事情。假设有一个集合List,将它的元素打印出来
原来的方式
/**
* @author kobe_t
* @date 2018/4/7 14:41
*/
public class Chapter1 {
@Test
public void test() {
List list = Arrays.asList("a", "b", "c");
// 方式1
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//方式2
for (String e : list) {
System.out.println(e);
}
}
}
方式1是jdk1.5之前循环方式,迭代时,可以通过下标获取元素;
方式2是jdk1.5的新特性,它其实是java的语法糖实现,也是俗称外部迭代,相比方式1,它写法更为简便
现在我们有了方式3,叮叮叮!那就是Lambda
List list = Arrays.asList("a", "b", "c");
// 方式1
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//方式2
for (String e : list) {
System.out.println(e);
}
// 方式3
list.forEach(l -> System.out.println(l));
// 方式3
list.forEach(l -> System.out.println(l));
看见了没,一行代码搞定!再也没有for() {}, 这种样板代码了,Lambda使我们理聚焦于业务代码,告诉它想得到什么,而不用去自己实现各种细节,只要传递我们的行为!
再看一下上面的代码
list.forEach(l -> System.out.println(l));
再进去看下这个方法
咦,有个Consumer,是什么东西呢,看看
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package java.util.function;
import java.util.Objects;
/**
* Represents an operation that accepts a single input argument and returns no
* result. Unlike most other functional interfaces, {@code Consumer} is expected
* to operate via side-effects.
*
* This is a functional interface
* whose functional method is {@link #accept(Object)}.
*
* @param the type of the input to the operation
*
* @since 1.8
*/
@FunctionalInterface
public interface Consumer {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
/**
* Returns a composed {@code Consumer} that performs, in sequence, this
* operation followed by the {@code after} operation. If performing either
* operation throws an exception, it is relayed to the caller of the
* composed operation. If performing this operation throws an exception,
* the {@code after} operation will not be performed.
*
* @param after the operation to perform after this operation
* @return a composed {@code Consumer} that performs in sequence this
* operation followed by the {@code after} operation
* @throws NullPointerException if {@code after} is null
*/
default Consumer andThen(Consumer super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
Ok,其中一个方法,一个默认方法。所以有了这样一个定义
只有一个抽象方法的接口,就是函数式接口
比如用于线程的Runable接口
// Lambda方式
Runnable runnable = () -> {};
// 匿名内部类
Runnable runnable2 = new Runnable() {
@Override
public void run() {
}
};
上面2种方式,创建Runnable接口;以前匿名内部类方式,发现了代码量多,关键是不能很好的表达程序的意图,而用Lambda方式,很简单;那么怎么识别Lambda表达式
// 形式1
Runnable runnable1 = () -> {
System.out.println("Hello");
System.out.println("World");
};
// 形式2
ActionListener listener = event -> {
System.out.println("This is a button listener:" + event);
};
// 形式3
BinaryOperator add = (x, y) -> x + y;
// 形式4
BinaryOperator lognAdd = (Long x, Long y) -> x + y;
上面几种形式都是Lambda表达式
形式1:Lambda表达式的主体不仅可以是一个表达式,也可以是一段代码块,用{}表明表达式何处开始,何处结束
形式2:Lambda表达式有一个参数event,且只有一个参数,可以省略参数的括号
形式3:有2个x,y,并且都是Long型的,返回x+y这个函数;注意并不是返回x+y的值,那么编译器怎么知道参数的类型呢,等下讲
形式4:也有2个参数x,y,但是声明了是Long型的,并返回x+y的函数代码
那么形式3与形式4有什么区别呢?没有区别!对于3这种来说,编译器会自动推断参数类型,也可以像类型4那样明确告知编译器
这就是java的类型推断
如果用过jdk1.7以上的话,多半知道<>
// 类型推断
List oldList = new ArrayList();
List strList = new ArrayList<>();
声明变量时,可以省略类型,jvm会自动推导类型,Lambda则更进一步,把参数的也省略了,一切交给虚拟机吧!
那如果Lambda没有声明类型呢
如上,我们的idea会提示,不能转为Object,因为代码毕竟需要一个地方来指明它的类型,否则它也懵B的
如何传递值
看一个例子
// 引用值
String name = "test";
Runnable runnable3 = new Runnable() {
@Override
public void run() {
System.out.println(name);
}
};
在线程里面引用了name变量,现在尝试修改name的值,也就是赋给另外的值,如下所示
发现了吗,编译器提示无法通过,且提示我们,变量是final的,不能修改,这就引出了一个定义
Lambda表达式引用的是即成事实的final变量,即意思着不能修改它!在Java8以前,这种变量是必须声明为final的,Java8放松了限制,但是它是既成事实的final变量,不能修改,用不用final声明,取结于编程的习惯;比如下面的代码可以编译通过
// 引用值
String name = "test";
// final声明
final String name2 = "test";
Runnable runnable3 = new Runnable() {
@Override
public void run() {
// 修改变量name的值
System.out.println(name + "," + name2);
}
};