Java8新特性-Lambda表达式

102 阅读9分钟

Lambda表达式是一种简洁高效的实现匿名内部类的一种方式.可以使代码变得更清晰、更灵活.大大增加代码可读性。

下面进行匿名内部类和Lambda表达式的比较,效果一目了然.

在java8之前使用匿名内部类是这样的

public void Test3() {
    List<Student> list = fitterStudent(students, new MyPredicate<Student>() {
        @Override
        public boolean judge(Student stu) {
            return stu.getSex().equals("男");
        }
    });
}

可以看到真正有用的只有return stu.getSex().equals("男");这一行代码,但是因为格式,确不得不把其他的好多行都写出来,毫无疑问这样的话,代码的可读性会大大降低.

下面来看一下使用Lambda表达式的效果

public void Test2() {
        List<Student> list = fitterStudent(students, (stu) -> stu.getSex().equals("女"));
    } 

看到效果了吧,原来的6=行代码,现在只需要1行.是不是很nb…

Lambda表达式的基本语法

java8中引入一个新的操作符”->”,称为箭头操作符或者为lambda操作符

箭头操作符将拉lambda表达式拆分成两个部分
左侧:参数列表,所实现接口抽象方法的参数列表(函数式接口)
右侧:所需执行的功能,称为lambdalambda表达式需要"函数式接口"(接口中只有一个抽象方法)支持
可以使用注解@FunctionalInterface 帮忙检查是否是函数式接口

语法格式一:无参数,无返回值

    /**
     * @Description 创建一个线程输出语句
     * @Author xw
     * @Date 10:20 2020/2/15
     * @Param []
     * @return void
     **/
    @Test
    public void test1() {
        Runnable r0 = new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类的方式:Hello Lambda!");
            }
        };
        r0.run();
        //语法格式一:无参数,无返回值
        Runnable r1 = () -> System.out.println("Lambda表达式的方法:Hello Lambda!");
        r1.run();
    }

语法格式二: 有一个参数,无返回值

    /**
     * @Description 参数是啥输出啥 
     * @Author xw
     * @Date 10:23 2020/2/15
     * @Param []
     * @return void
     **/
    @Test
    public void test2(){
        //声明一个消费型接口(有一个参数,但是无返回值)调用accept方法,传入一个参数
        Consumer<String> con = (x) -> System.out.println(x);
        //语法格式二: 有一个参数,无返回值
        con.accept("Hello Lambda!");
    }

语法格式三: 若只有一个参数则参数的小括号可以不写,(推荐还是写上)

Consumer<String> con = (x) -> System.out.println(x);
上面的语句等价于
Consumer<String> con = x -> System.out.println(x);

语法格式四: 有多个参数,并且lambda体中有多个语句,并且有返回值

    /**
     * @Description 比较两个整数
     * @Author xw
     * @Date 10:38 2020/2/15
     * @Param []
     * @return void
     **/
    @Test
    public void test4(){
        //用于比较的接口Comparable,提供了一个比较的方法
        Comparator<Integer> com = (x, y) -> {
            //有多个参数,并且lambda体中有多个语句,必须使用大括号,并且有返回值
            System.out.println("x = " + x);
            System.out.println("y = " + y);
            //Integer类就是Comparator的实现类
            return Integer.compare(x,y);
        };
        System.out.println(com.compare(1520));
    }

语法格式五: 有多个参数并且有返回值,只有一条语句(return 和大括号都可以省略)

Comparator<Integer> com = (x, y) -> {
    return Integer.compare(x,y);
};
上面的写法等价于
Comparator<Integer> com = (x,y) ->  Integer.compare(x, y);

语法格式六: lambda表达式的参数列表数据类型可以不写,jvm自动推断(推荐不写)

Comparator<Integer> com = (Integer x,Integer y) ->  Integer.compare(x, y);
等价于
Comparator<Integer> com = (x,y) ->  Integer.compare(x, y);

总结:

  • 左右遇一括号省‘->’左边参数列表中若只有一个参数,那么小括号可以省略,’->’右边如果只有一条语句,那么大括号可以省略
  • 左侧推断类型省‘->’左边参数的参数类型可以省略
  • 右侧一条return省如果Lambda表达式具有返回值,并且只有一条语句,那么可以省略’return’关键字

Lambda表达式既然这么好用,那么是不是什么时候都可以应用呢?

不是的,Lambda表达式必须要有函数式接口的支持,即接口中必须只有一个抽象方法

那么问题来了,难道每次使用Lambda表达式都需要自己写一个函数式接口?

当然不是了,java8现在内置了四大基本的函数式接口,而且还有好多函数式接口,所以没有想象的那么麻烦,下面就开始介绍java8内置的四大函数式接口

1.Consumer 消费型接口(有一个参数,但是无返回值) void accept(T t);

@Test
public void test7() {
    happy(20000, (money) -> System.out.println("我今天花了"+money+"元"));
}
/**
 * @Description happy的作用就是想参数传递给 Consumer
 * @Author xw
 * @Date 10:54 2020/2/15
 * @Param [money, consumer]
 * @return void
 **/
public void happy(double money,Consumer<Double> consumer) {
    consumer.accept(money);
}

不写happy方法可以直接这样写

    Consumer<Double> consumer = (x) ->{
        System.out.println("我今天花了"+x+"元");
    };
    consumer.accept(2000d);

2.Supplier 供给型接口(无参数,但是有返回值) T get();

@Test
public void test8() {
    //获得5个0-100的随机数
    List<Integer> numList = getRandomNum(5, () -> (int)(Math.random()*100));
    numList.forEach(System.out::println);
}

/**
 * @Description 获得num个sup.get()得到的结果
 * @Author xw
 * @Date 11:05 2020/2/15
 * @Param [num, sup]
 * @return java.util.List<java.lang.Integer>
 **/
public List<Integer> getRandomNum(int num, Supplier<Integer> sup){
    List<Integer> list = new ArrayList<>();
    for (int i = 0; i < num; i++) {
        list.add(sup.get());
    }
    return list;
}

3.Function 函数型接口(有一个参数,有返回值)R apply(T t);

@Test
public void test9() {
    String result = strHandler("hello ", (str) -> {
        return str+= "Lambda";
    });
    System.out.println(result);
}

/**
 * @Description  将str传递给函数型接口
 * @Author xw
 * @Date 11:10 2020/2/15
 * @Param [str 前缀, fun]
 * @return java.lang.String
 **/
public String strHandler(String str, Function<StringString> fun) {
    return fun.apply(str);
}

4.Predicate 断言型接口(有一个参数,返回值为波尔类型) boolean test(T t);

@Test
public void Test10() {
    List<String> list = Arrays.asList("Hello","world","my","name","is","moti");
    List<String> result = filterStr(list, (str) -> {
        //判断字符串是否含有o
        return str.contains("o");
    });
    result.forEach(System.out::println);
}

/**
 * @Description 根据 断言型接口提供的条件,筛选集合
 * @Author xw
 * @Date 11:14 2020/2/15
 * @Param [list, pre]
 * @return java.util.List<java.lang.String>
 **/
public List<StringfilterStr(List<String> list, Predicate<String> pre){
    List<String> resultList = new ArrayList<>();
    for (String string : list) {
        if(pre.test(string)) {
            resultList.add(string);
        }
    }
    return resultList;
}

java8之后的接口可以这么写

@FunctionalInterface
interface myInterface{
    //只有一个没实现的方法,这个接口就叫做函数式接口
    void fun1();
    //默认实现
    default int fun2(int a,int b){
        return a+b;
    }
    //静态方法
    static int fun3(int a,int b){
        return a*b;
    }
} 

Java8 还有好多关于以上接口的子接口,有兴趣的可以自己去查查