参考教材:《Java 核心技术 卷一》

前言

在JAVA SE 8 之前,在java中传递一个代码块并不容易,不能直接传递代码块,因为java是一种面向对象的语言,所以想传递代码块需要把所需传递的代码写在类的方法里,然后通过传递类的实例(即对象)才能达到传递代码块的目的。
为了解决这一问题,JAVA SE 8 加入lambda表达式这一新特性,可以用一种简洁的语法来定义代码块,并把这个代码块传递给某个对象,这个代码块可以在将来的某个时间被调用一次或者多次调用。

语法

lambda表达式的一般构成:参数, 箭头(->) 以及一个表达式 如果代码要完成的计算无法放在一个表达式中,就可以像写方法一样,把这些代码放在 {}里,还可以显式的使用return语句。
即:
(parameters) -> expression(parameters) ->{ statements; } 实例:

//参数可以为空,但仍然需要提供括号
()-> System.out.println("haha");
//多行代码时需要用{}包裹,还可以使用return语句
(int x)->{ System.out.println(x);return x*x;}
//可以不指定的返回类型。返回类型总是会由上下文推导得出
(String first,String second)->first.length()-second.length();
//当参数类型可以通过上下文推导出来,参数类型可以省略
//比如下面,lambda表达式被赋值给一个字符串比较器,所以编译器可以推导出
//参数类型是String
Comparator<String> comp = (first,second)->first.length()-second.length();
//当参数只有一个,且类型可以由上下文推导而出的时候,小括号可以省略
ActionListener listener = event -> System.out.println("现在的时间为:"+new Date());

函数式接口的编写

java已经提供了支持lambda表达式的接口,比如上面的ActionListener,Comparator,那么问题来了,接口符合什么要求就可以支持lambda表达式呢? 要求很简单:接口有且最多只能有一个抽象方法(ps:可以有其他默认方法,但抽象方法只能有一个,否则lambda表达式赋值的时候会因为无法确定是哪个抽象方法而报错)。
现在我们就试着自己编写一个函数式接口,并试着给他传入一个lambda表达式,看能不能正常接收。

public  interface Person{
    public void test(String s);
}

好了,这样就写好了一个支持lambda表达式的函数式接口。现在是不是发现这个接口没有任何特殊之处,就像我们平时写的一些接口。(换句话说,你平时写的一些接口就支持lambda表达式)。
下面我们就编写一个lambda表达式传入test方法。

public class PersonTest {
    public static void main(String[] args){
        //向p对象中的test方法传入lambda表达式
        //下面两个表达式等价
        //因为参数只有一个,且参数类型可从test方法的定义推出
        //所以可以省略小括号和参数类型
        Person p = s -> System.out.println(s);
        //Person p = (String s)-> System.out.println(s);

        //调用p对象的test方法时,会执行刚刚传入的lambda表达式
        p.test("lambda表达式测试");
    }
}

执行结果如下:

android java lambda表达式 java lambda表达式语法_编程语言


重新写下Person接口再测试一下

public  interface Person{
    //把刚刚的抽象方法改成默认方法
    public default void test(String s){
        System.out.println("这是接口的默认方法,传入的字符串为:"+s);
    };
    //重新定义一个有返回值抽象的方法
    public int factorial(int i);
}
public class PersonTest {
    public static void main(String[] args){

        Person p = i -> {
            //返回i的阶乘
            int sum=1;
            while (i>0){
                sum*=i;
                i--;
            }
            return sum;
        };
        //调用默认方法
        p.test("哈哈哈");
        //调用抽象方法
        int sum = p.factorial(5);
        System.out.println("5的阶乘为"+sum);
    }
}

运行结果

android java lambda表达式 java lambda表达式语法_java_02

lambda表达式的用途及实例

使用lambda表示式的特点是延迟执行,毕竟如果是想立即执行代码的话,完全可以直接执行,不用把它放到lambda表达式里。(所以我上面写的例子可能体现不出lambda表达式的特点,只是为了让读者了解lambda表达式的编写及调用)
那哪些情况需要代码延迟执行呢 ? ,比如下列情况

  1. 在一个单独的线程中运行代码;
  2. 多次运行代码;
  3. 在算法的适当位置运行代码(例如, 排序中的比较操作);
  4. 发生某种情况时执行代码(如, 点击了一个按钮, 数据到达, 等等)
  5. 只在必要时才运行代码。

示例一:构造线程

//使用匿名类方式构建线程
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("hahahaha");
            }
        });
        thread.start();
        //使用lambda表达式
        Thread thread = new Thread(()-> System.out.println("hahahaha"));
        thread.start();

示例二:列表迭代

List<String> languages = Arrays.asList("Java","C++","Python","JavaScript","C#");
        //一般的迭代遍历方式
        for (String language:languages){
            System.out.println(language);
        }
        //使用lambda表达式
        languages.forEach(n-> System.out.println(n));

示例三:数组排序

String[] languages = new String[]{"Java","C++","Python","JavaScript","C#"};
        //使用匿名类方式
        Arrays.sort(languages, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //根据长度排序
                return o1.length()-o1.length();
            }
        });
        //使用lambda形式
        Arrays.sort(languages,(first,second)->first.length()-second.length());