快捷搜索:

什么是Lambda表达式,它有什么作用?

目录

一、Lambda表达式概念 二、函数式接口 三、Lambda表达式语法 四、Lambda表达式作用

一、Lambda表达式概念

lambda表达式是JAVA8中提供的一种新的特性,它支持JAVA也能进行简单的“函数式编程”。 它是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda 是一个匿名函数,可以把 Lambda表达式 理解为是一段可以传递的代码 (将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升 ,JDK 也提供了大量的内置函数式接口供我们使用,使得 Lambda 表达式的运用更加方便、高效。

二、函数式接口

什么是函数式接口?这个是我们理解Lambda表达式的重点,也是产生lambda表达式的“母体”,这里我们引用一个比较容易理解的说法:函数式接口是 一个只有一个抽象方法(不包含object中的方法)的接口。 这个需要说明一点,就是在Java中任何一个对象都来自Object 所有接口中自然会继承自Object中的方法,但在判断是否是函数式接口的时候要排除Object中的方法,下面举几个例子如下:

//这个是函数式接口
interface eat 
{
          
     
   void eatFood();    
}
 
 
//这个不是是函数式接口
interface eat
{
          
     
  default void eatFood()
  {
          
   
     System.out.println("hello");
  };    
}
 
 
 
//这个是一个函数式接口
interface eat 
{
          
     
 void eatFood();    
 String toString();
}

对于是否是函数式接口,java8中也提供了一个专用的注解:@FunctionalInterface。通过这个注解,可以确定是否是函数式接口:

//此处会报编译错误
@FunctionalInterface
interface eat 
{
          
     
  default void eatFood()
  {
          
   
    System.out.println("hello");
  };    
}

三、Lambda表达式语法

Lambda 表达式的基础语法 : Java8 中引入了一个新的操作符 “->” 该操作符称为箭头操作符或 Lambda 操作符,箭头操作符将 Lambda 表达式拆分成两部分 : 左侧 : Lambda 表达式的参数列表 右侧 : Lambda 表达式中所需执行的功能, 即 Lambda 体

语法格式一 : 无参数,无返回值 () -> System.out.println(“Hello Lambda!”);

语法格式二 : 有一个参数,并且无返回值 (x) -> System.out.println(x)

语法格式三 : 若只有一个参数,小括号可以省略不写 x -> System.out.println(x)

Consumer con = (x) -> System. out .println(x); con.accept( “啦啦啦,我是卖报的小行家” );

语法格式四 : 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句 Comparator com = (x, y) -> { System.out.println(“函数式接口”); return Integer.compare(x, y); };

语法格式五 : 若 Lambda 体中只有一条语句, return 和 大括号都可以省略不写 Comparator com = (x, y) -> Integer.compare(x, y);

语法格式六 : Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断” (Integer x, Integer y) -> Integer.compare(x, y); 注 : Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”。

四、Lambda表达式作用

1.替代匿名内部类

毫无疑问,lambda表达式用得最多的场合就是替代匿名内部类,而实现Runnable接口是匿名内部类的经典例子。lambda表达式的功能相当强大,用()->就可以代替整个匿名内部类!请看代码:

如果使用匿名内部类:

@Test
    public void oldRunable() {
          
   
        new Thread(new Runnable() {
          
   
            @Override
            public void run() {
          
   
                System.out.println("The old runable now is using!");
            }
        }).start();
    }

而如果使用lambda表达式:

@Test
    public void runable() {
          
   
        new Thread(() -> System.out.println("Its a lambda function!")).start();
    }

最后的输出:

The old runable now is using!
Its a lambda function!

这就是lambda表达式的好处之一,用极少的代码完成了之前一个类做的事情!

2.使用lambda表达式对集合进行迭代

Java的集合类是日常开发中经常用到的,甚至说没有哪个java代码中没有使用到集合类。。。而对集合类最常见的操作就是进行迭代遍历了。请看对比:

@Test
    public void iterTest() {
          
   
        List<String> languages = Arrays.asList("java","scala","python");
        //before java8
        for(String each:languages) {
          
   
            System.out.println(each);
        }
        //after java8
        languages.forEach(x -> System.out.println(x));
        languages.forEach(System.out::println);
    }

如果熟悉scala的同学,肯定对forEach不陌生。它可以迭代集合中所有的对象,并且将lambda表达式带入其中。

languages.forEach(System.out::println);

这一行看起来有点像c++里面作用域解析的写法,在这里也是可以的。

3.Stream操作

我们来认识一下Lambda的好兄弟Stream,这里的Stream并不是输入流和输出流(IO),而它也是jdk8后的新特性,是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。 Stream就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。 简单来讲,Stream就是JAVA8提供给我们的对于元素集合统一、快速、并行操作的一种方式。它能充分运用多核的优势,以及配合lambda表达式、链式结构对集合等进行许多有用的操作。 在这里,我们只简单介绍几个主要方法:

1)filter(过滤)

//过滤18岁以上的人
List persons = …
Stream personsOver18 = persons.stream().filter(p -> p.getAge() > 18);

2)Map(映射)

如果想通过某种操作把一个流中的元素转化成新的流中的元素,可以使用 map() 方法。

public class MapStreamDemo {
          
   
    public static void main(String[] args) {
          
   
        List<String> list = new ArrayList<>();
        list.add("周杰伦");
        list.add("王力宏");
        list.add("陶喆");
        list.add("林俊杰");
        Stream<Integer> stream = list.stream().map(String::length);
        stream.forEach(System.out::println);
    }
}

map() 方法接收的是一个 Function(Java 8 新增的一个函数式接口,接受一个输入参数 T,返回一个结果 R)类型的参数,此时参数 为 String 类的 length 方法,也就是把 Stream 的流转成一个 Stream 的流。

程序输出的结果如下所示:

3
3
2
3

3)limit(截断)

对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素

4)distinct(去重)

对于Stream中包含的元素进行去重操作(去重逻辑依赖元素的equals方法),新生成的Stream中没有重复的元素

5)count(统计)

count方法是一个流的终点方法,可使流的结果最终统计,返回int,比如我们计算一下满足18岁的总人数

int countOfAdult=persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .count();

6)Collect(收集流的结果)

collect方法也是一个流的终点方法,可收集最终的结果

List adultList= persons.stream()
                       .filter(p -> p.getAge() > 18)
                       .map(person -> new Adult(person))
                       .collect(Collectors.toList());

4.与函数式接口Predicate配合

除了在语言层面支持函数式编程风格,Java 8也添加了一个包,叫做 java.util.function。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。Predicate接口非常适用于做过滤。

public static void filterTest(List<String> languages, Predicate<String> condition) {
          
   
    languages.stream().filter(x -> condition.test(x)).forEach(x -> System.out.println(x + " "));
}

public static void main(String[] args) {
          
   
    List<String> languages = Arrays.asList("Java","Python","scala","Shell","R");
    System.out.println("Language starts with J: ");
    filterTest(languages,x -> x.startsWith("J"));
    System.out.println("
Language ends with a: ");
    filterTest(languages,x -> x.endsWith("a"));
    System.out.println("
All languages: ");
    filterTest(languages,x -> true);
    System.out.println("
No languages: ");
    filterTest(languages,x -> false);
    System.out.println("
Language length bigger three: ");
    filterTest(languages,x -> x.length() > 4);
}

最后的输出结果:

Language starts with J: 
Java 

Language ends with a: 
Java 
scala 

All languages: 
Java 
Python 
scala 
Shell 
R 

No languages: 

Language length bigger three: 
Python 
scala 
Shell

可以看到,Stream API的过滤方法也接受一个Predicate,这意味着可以将我们定制的 filter() 方法替换成写在里面的内联代码,这也是lambda表达式的魔力!

参考文档: 1.

经验分享 程序员 微信小程序 职场和发展