目 录CONTENT

文章目录

java函数式接口(lambda表达式)

在水一方
2021-12-22 / 0 评论 / 0 点赞 / 1,064 阅读 / 5,824 字 / 正在检测是否收录...

Java8新特性

Java8的新特性主要是lambda表达式和流,它是推动 Java 8 发布的最重要新特性,允许把函数作为一个方法的参数(函数作为参数传递进方法中)使用 Lambda 表达式可以使代码变的更加简洁紧凑

为什么要使用lambda

Lambda是一段可以传递的代码(能够做到将代码像数据一样进行传递)。使用Lambda表达式能够写出更加简洁、灵活的代码。并且,使用Lambda表达式能够使Java的语言表达能力得到提升。

为什么要学函数式编程

面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。现实世界中,数据和行为并存,程序也是如此,因此这两种编程方式我们都得学

和匿名内部类的比较

设计匿名内部类的目的,就是为了方便 Java 程序员将代码作为数据传递。不过,匿名内部类还是不够简便

入门示例:

button.addActionListener(event -> System.out.println("button clicked"));

说明:event 是参数名,-> 将参数和 Lambda 表达式的主体分开,在Lambda 表达式中无需指定参数类型

如何辨别Lambda表达式

简单示例:
1 Lambda 表达式不包含参数,使用空括号 () 表示没有参数。该 Lambda 表达式实现了 Runnable 接口,该接口也只有一个 run 方法,没有参数,且返回类型为 void

Runnable noArguments = () -> System.out.println("Hello World");

2 Lambda 表达式包含且只包含一个参数,可省略参数的括号

ActionListener oneArgument = event -> System.out.println("button clicked");

3 Lambda 表达式的主体不仅可以是一个表达式,而且也可以是一段代码块,使用大括号({})将代码块括起来,如下所示。该代码块和普通方法遵循的规则别无二致,可以用返回或抛出异常来退出。只有一行代码的 Lambda 表达式也可使用大括号,用以明确 Lambda表达式从何处开始、到哪里结束

Runnable multiStatement = () -> {
System.out.print("Hello");
System.out.println(" World");
};

4 Lambda 表达式也可以表示包含多个参数的方法,如下所示。这时就有必要思考怎样去阅读该 Lambda 表达式。这行代码并不是将两个数字相加,而是创建了一个函数,用来计算两个数字相加的结果。变量 add 的类型是BinaryOperator,它不是两个数字的和,而是将两个数字相加的那行代码。

BinaryOperator<Long> add = (x, y) -> x + y;

也可指定参数类型:
BinaryOperator addExplicit = (Long x, Long y) -> x + y;

将 Lambda 表达式赋值给一个局部变量,或传递给一个方法作为参数,局部变量或方法参数的类型就是 Lambda 表达式的目标类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的函数接口是只有一个抽象方法的接口,用作 Lambda 表达式的类型

java中重要的函数接口:

image.png

第二部分:流(Stream API)

什么是流:流是从支持数据处理操作的源生成的元素序列,源可以是数组、文件、集合、函数。流不是集合元素,它不是数据结构并不保存数据,它的主要目的在于计算
作用:流使程序员得以站在更高的抽象层次上对集合进行操作,和
Iterator 类似,Stream 是一种内部迭代方式。将 Lambda 表达式和Stream 上的方法结合起来,可以完成很多常见的集合操作。

api文档地址:https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html

Java 程序员在使用集合类时,一个通用的模式是在集合上进行迭代,然后处理返回的每一个元素
List接口中的stream()函数

default Stream<E> stream() {
     return StreamSupport.stream(spliterator(), false);
   }

Stream接口当中包含的方法:
image.png

  • Stream接口中的方法的参数就是一个函数式接口

测试案例1:


 @Test
    public void stream01(){
        List<Integer> list = new ArrayList<>();
        list.add(1232);
        list.add(435345);
        list.add(657868);
        list.stream().forEach(i -> System.out.println(i));
        System.out.println(list.stream().count()); // count函数用 
        于输出集合中元素个数
        
    }
打印结果:
1232
435345
657868

stream.filter一般适用于list集合,从集合中查询想要的数据

 list.stream().forEach(System.out::println);
@Test
    public void stream02(){
        List<Integer> list = new ArrayList<>();
        list.add(1232);
        list.add(435345);
        list.add(657868);
        list.add(null);
 	list.stream().filter(StringUtils::isNotBlank).forEach(System.out::println);   //将等于null的过滤出来再打印
        list.stream().filter(i -> i>12344).forEach(j -> System.out.println(j));
    
    }

这个意思是指:筛选出list集合中值大于12344的结果,然后将其打印出来
输出结果:
435345
657868

另外一种写法:


 @Test
    public void stream02(){
        List<Integer> list = new ArrayList<>();
        list.add(1232);
        list.add(435345);
        list.add(657868);
        List<Integer> li = list.stream().filter(i -> i>12344).collect(Collectors.toList());
        li.forEach(j -> System.out.println(j));
    }

返回结果:
435345
657868

map流映射

map 操作就可以使用该函数,将一个流中的值转换成一个新的流。

@Test
    public void map(){
        List<String> list = new ArrayList<>();
        list.add("sssasdf");
        list.add("ahj");
        list.add("kdf");
        list.stream().map(r -> r.toUpperCase()).forEach(j -> System.out.println(j));
}
得到的结果:
SSSASDF
AHJ
KDF

对map的遍历:


 @Test
    public void map1(){
        Map<String, String> map = new HashMap<>();
        map.put("name", "啊啊");
        map.put("age", "11");
        map.forEach((k,v) -> {
            if ("name".equals(k)) {
                System.out.println(k);
            }
        });
        
    }
输出结果:name


如果仅需打印的话:
 map.forEach((k,v) -> System.out.println(k)); //打印K值
 map.forEach((k,v) -> System.out.println(v)); //打印V值


方法引用

Java 8 为其提供了一个简写语法,叫作方法引用,帮助程序员重
用已有方法。用方法引用重写上面的 Lambda 表达式,代码如下:
Artist::getName
标准语法为 Classname::methodName。需要注意的是,虽然这是一个方法,但不需要在后面加括号,因为这里并不调用该方法。我们只是提供了和 Lambda 表达式等价的一种结构,在需要时才会调用。凡是使用 Lambda 表达式的地方,就可以使用方法引用。

如果你想使用 Lambda 表达式创建一个 Artist 对象,可能会写出如下代码:
(name, nationality) -> new Artist(name, nationality)
使用方法引用,上述代码可写为:
Artist::new
这段代码不仅比原来的代码短,而且更易阅读。Artist::new 立刻告诉程序员这是在创建一个 Artist 对象,程序员无需看完整行代码就能弄明白代码的意图。另一个要注意的地方是方法引用自动支持多个参数,前提是选对了正确的函数接口。

如果List的值为null,则使用list.stream会报错,如果list的size为0,就是里面没有任何元素,是可以正常使用stream

求一个List中的最大值:

    @Test
   public void demo(){
        List<Integer> intList = new ArrayList<>();
        intList.add(5);
        intList.add(10);
        intList.add(15);
        System.out.println(intList.stream().max(Integer::compare).get());
        
   }

打印结果:15

findAny()+ifPresent方法过滤集合中的单条结果(实用)


 @Test
   public void doDemo(){
       List<User> list = new ArrayList();
       User u1 = new User();
       u1.setUsername("zhangsan");
       list.add(u1);
       User u2 = new User();
       u2.setUsername("list");
       list.add(u2);
       User u3 = new User();
       u3.setUsername(null);
       u3.setAge(15);
       list.add(u3);
       User u4 = new User();
       u4.setUsername("zhangsan");
       list.add(u4);
       list.stream().filter(r -> "con".equals(r.getUsername())).findAny().ifPresent(y->{
           System.out.println(y.getAge());
       });

   }

未打印结果:
原因是用户名中没有匹配到名为"con"的记录,所以ifPresent中的内容没有执行

重点:

    User uu = list.stream().filter(r -> "zhangsan".equals(r.getUsername())).findAny().orElse(u1);
       System.out.println(uu.toString());

  • orElse(T other) 不论容器是否为空,只要调用该方法, 则返回值就是u1
  • orElseGet(Supplier<? extends T> supplier) 只有当容器为空时,才调用supplier.get()方法产生对象
       User uu = list.stream().filter(r -> "zhangsan".equals(r.getUsername())).findAny().orElseGet(()->{
           User user = new User();
           return user;
       });

这里需要注意orElseGet后面这里的lambda表达式的写法

关于Optional<T>

    public String getCourse(Student stu) throws Exception {
 
        return Optional.ofNullable(stu)
                .map(s -> s.getTeacher())
                .map(t -> t.getCourse())
                .orElse("无课程");
    }

这是利用Java 8 提供的Optional类,实现对非空等条件的代码简化。另外,这也用到了Java 8函数式编程的语法,所以可以实现链式调用,代码流畅、可读性高

如果不用的话,原始的写法,会有许多的if判断

    public String getCourse(Student stu) throws Exception {//该方法根据学生获取其课程
        if(stu != null){ // 这里必须写非空判断
            Teacher t = stu.getTeacher();//获取学生的老师
            if(t != null){ // 这里必须写非空判断
                return t.getCourse();//返回老师的课程
            }
        }
        return "无课程";
    }

参考文章:https://blog.csdn.net/liudun_cool/article/details/116521317

相关参考文章链接:https://mp.weixin.qq.com/s/7l6FVTti9yKHHVUWIyemtA

github上关于lambda的练习案例:
https://github.com/RichardWarburton/java-8-lambdas-exercises

0

评论区