函数式编程与Lambda表达式

简介: 函数式编程与Lambda表达式

函数式编程与Lambda表达式


1 函数式编程的优势


1.1 函数式编程思想

4ea9671f50ce4e85b44a31f8b5b87ad6.png


在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什 么事情”。编程中的函数,也有类似的概念,你调用我的时候,给我实参为形参赋值,然后通过运行方法体,给你返回一个结果。对于调用者来做,关注这个方法具备什么样的功能。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法——强调做什么,而不是以什么形式做。


   面向对象的思想:

       做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情.


   函数式编程思想:

       只要能获取到结果,谁去做的不重要,重视的是结果,不重视过程


Java8引入了Lambda表达式之后,Java也开始支持函数式编程。


Lambda表达式不是Java最早使用的,很多语言就支持Lambda表达式,例如:C++,C#,Python,Scala等。如果有Python或者Javascript的语言基础,对理解Lambda表达式有很大帮助,可以这么说lambda表达式其实就是实现SAM接口的语法糖,使得Java也算是支持函数式编程的语言。Lambda写的好可以极大的减少代码冗余,同时可读性也好过冗长的匿名内部类。

   备注:“语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的for-each语法,其实

   底层的实现原理仍然是迭代器,这便是“语法糖”。从应用层面来讲,Java中的Lambda可以被当做是匿名内部

   类的“语法糖”,但是二者在原理上是不同的。



1,2 冗余的匿名内部类


当需要启动一个线程去完成任务时,通常会通过java.lang.Runnable接口来定义任务内容,并使用java.lang.Thread类来启动该线程。代码如下:

public class Demo01 {
    public static void main(String[] args) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {// 覆盖重写抽象方法
                System.out.println("使用匿名内部类重写的run方法");
            }
        };
        new Thread(runnable).start(); // 启动线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("使用匿名内部类不创建对象重写的run方法");
            }
        }).start();
    }
}


本着“一切皆对象”的思想,这种做法是无可厚非的:首先创建一个Runnable接口的匿名内部类对象来指定任务内容,再将其交给一个线程来启动。


代码分析:


对于Runnable的匿名内部类用法,可以分析出几点内容:


   Thread类需要Runnable接口作为参数,其中的抽象run方法是用来指定线程任务内容的核心;

   为了指定run的方法体,不得不需要Runnable接口的实现类;

   为了省去定义一个RunnableImpl实现类的麻烦,不得不使用匿名内部类;

   必须覆盖重写抽象run方法,所以方法名称、方法参数、方法返回值不得不再写一遍,且不能写错;

   而实际上,似乎只有方法体才是关键所在。



1.3 编程思想转换


做什么,而不是谁来做,怎么做


我们真的希望创建一个匿名内部类对象吗?不。我们只是为了做这件事情而不得不创建一个对象。我们真正希望做的事情是:将run方法体内的代码传递给Thread类知晓。


传递一段代码——这才是我们真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。那,有没有更加简单的办法?如果我们将关注点从“怎么做”回归到“做什么”的本质上,就会发现只要能够更好地达到目的,过程与形式其实并不重要。


生活举例:


98af2d2afe7b4d389854e9c784eaddd6.png


当我们需要从北京到上海时,可以选择高铁、汽车、骑行或是徒步。我们的真正目的是到达上海,而如何才能到达上海的形式并不重要,所以我们一直在探索有没有比高铁更好的方式——搭乘飞机。



而现在这种飞机(甚至是飞船)已经诞生:2014年3月Oracle所发布的Java 8(JDK 1.8)中,加入了Lambda表达式的重量级新特性,为我们打开了新世界的大门。



1.4 体验Lambda的更优写法


借助Java 8的全新语法,上述Runnable接口的匿名内部类写法可以通过更简单的Lambda表达式达到等效:

public class Demo01 {
    public static void main(String[] args) {
        new Thread(() -> {
            System.out.println("使用Lambda表达式重写的run方法");
        }).start();
        new Thread(() -> System.out.println("使用简化的Lambda表达式重写的run方法")).start();
    }
}

这段代码和刚才的执行效果是完全一样的,可以在1.8或更高的编译级别下通过。从代码的语义中可以看出:我们启动了一个线程,而线程任务的内容以一种更加简洁的形式被指定。


不再有“不得不创建接口对象”的束缚,不再有“抽象方法覆盖重写”的负担,就是这么简单!



2 函数式接口


lambda表达式其实就是实现SAM接口的语句,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。


其实只要满足“SAM”特征的接口都可以称为函数式接口,都可以使用Lambda表达式,但是如果要更明确一点,最好在声明接口时,加上@FunctionalInterface。一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法,否则将会报错。


之前的SAM接口中,标记了@FunctionalInterface的函数式接口的有:Runnable,Comparator,FileFilter。


Java8在java.util.function新增了很多函数式接口:主要分为四大类,消费型、供给型、判断型、功能型。基本可以满足我们的开发需求。当然你也可以定义自己的函数式接口。



2.1 自定义函数式接口


只要确保接口中有且仅有一个抽象方法即可:

修饰符 interface 接口名称 {
    public abstract 返回值类型 方法名称(可选参数信息);
    // 其他非抽象方法内容
}


接口当中抽象方法的 public abstract 是可以省略的


例如:声明一个计算器Calculator接口,内含抽象方法calc可以对两个int数字进行计算,并返回结果:

public interface Calculator {
    int calc(int a, int b);
}


在测试类中,声明一个如下方法:

public static void invokeCalc(int a, int b, Calculator calculator) {
    int result = calculator.calc(a, b);
    System.out.println("结果是:" + result);
}


下面进行测试:

public static void main(String[] args) {
    invokeCalc(1, 2, (int a,int b)-> {return a+b;});
    invokeCalc(1, 2, (int a,int b)-> {return a-b;});
    invokeCalc(1, 2, (int a,int b)-> {return a*b;});
    invokeCalc(1, 2, (int a,int b)-> {return a/b;});
    invokeCalc(1, 2, (int a,int b)-> {return a%b;});
    invokeCalc(1, 2, (int a,int b)-> {return a>b?a:b;});
}



2.2 函数式接口的分类

除了我们可以自定义函数式接口之外,jdk也给我们内置了一些函数式接口,具体分类如下


2.2.1 消费型接口

消费型接口的抽象方法特点:有形参,但是返回值类型是void

image.png


2.2.2 供给型接口

这类接口的抽象方法特点:无参,但是有返回值

image.png


2.2.3 断言型接口

这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。

image.png


2.2.4 功能型接口

这类接口的抽象方法特点:既有参数又有返回值


image.pngimage.png


3 Lambda表达式语法


Lambda表达式是用来给【函数式接口】的变量或形参赋值用的。

其实本质上,Lambda表达式是用于实现【函数式接口】的“抽象方法”


Lambda表达式语法格式

(形参列表) -> {Lambda体}
• 1

说明:


   (形参列表)它就是你要赋值的函数式接口的抽象方法的(形参列表),照抄

   {Lambda体}就是实现这个抽象方法的方法体

   ->称为Lambda操作符(减号和大于号中间不能有空格,而且必须是英文状态下半角输入方式)

优化:Lambda表达式可以精简

   当{Lambda体}中只有一句语句时,可以省略{}和{;}

   当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么return也可以省略,但是如果{;}没有省略的话,return是不能省略的

   (形参列表)的类型可以省略

   当(形参列表)的形参个数只有一个,那么可以把数据类型和()一起省略,但是形参名不能省略

   当(形参列表)是空参时,()不能省略


示例代码:

ps:以下演示使用了Junit进行单元测试,不明白的可以先学习一下Junit


public class TestLambdaGrammer {
  @Test
  public void test1(){
    //用Lambda表达式给Runnable接口的形参或变量赋值
    /*
     * 确定两件事,才能写好lambda表达式
     * (1)这个接口的抽象方法是否需要传入参数
     *    public void run()
     * (2)这个抽象方法的实现要干什么事
     *    例如:我要打印“hello lambda"
     */
    Runnable r = () -> {System.out.println("hello lambda");};
        //简化,省略了{;}
        Runnable r = () -> System.out.println("hello lambda");
  }
  @Test
  public void test2(){
    String[] arr = {"hello","Hello","java","chai"};
    //为arr数组排序,但是,想要不区分大小写
    /*
     * public static <T> void sort(T[] a,Comparator<? super T> c)
     * 这里要用Lambda表达式为Comparator类型的形参赋值
     * 
     * 两件事:
     * (1)这个接口的抽象方法:  int compare(T o1, T o2)
     * (2)这个抽象方法要做什么事?
     *    例如:这里要对String类型的元素,不区分大小写的比较大小
     */
    Arrays.sort(arr, (String o1, String o2) -> {return o1.compareToIgnoreCase(o2);});
    //省略了{return ;}
    //Arrays.sort(arr, (String o1, String o2) ->  o1.compareToIgnoreCase(o2));
    //省略了两个String
    Arrays.sort(arr, (o1, o2) ->  o1.compareToIgnoreCase(o2));
    //打印arr
    for (String string : arr) {
      System.out.println(string);
    }
  }
  @Test
  public void test3(){
    ArrayList<String> list = new ArrayList<>();
    list.add("hello");
    list.add("java");
    list.add("world");
    /*
     * JDK1.8给Collection系列的集合,准确的讲是在Iterable接口中,增加了一个默认方法
     *    default void forEach(Consumer<? super T> action) 
     * 这个方法是用来遍历集合等的。代替原来的foreach循环的。
     * 
     * 这个方法的形参是Consumer接口类型,它是函数式接口中消费型接口的代表
     * 现在调用这个方法,想要用Lambda表达式为Consumer接口类型形参赋值
     * 
     * 两件事:
     * (1)它的抽象方法:  void  accept(T t)
     * (2)抽象方法的实现要完成的事是什么
     *    例如:这里要打印这个t
     */
//    list.forEach((String t) -> {System.out.println(t);});
    //省略{;}
//    list.forEach((String t) -> System.out.println(t));
    //省略String
//    list.forEach((t) -> System.out.println(t));
    //省略形参的()
    list.forEach(t -> System.out.println(t));
  }
}


4 Lambda表达式练习使用


4.1 消费型接口


4.1.1 自定义消费型接口测试


定义接口:

package Jdk8.Lambda;
import java.util.function.Consumer;
public class LambdaManager {
    public static void userConsumer(String str, Consumer<String> consumer) {
        consumer.accept(str);
    }
}


接口测试:

    //自定义消费性接口
    @Test
    public void testConsumer() {
//        LambdaManager.userConsumer("你好", new Consumer<String>() {//使用匿名内部类实现接口
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        });//和下边效果相同
        LambdaManager.userConsumer("你好", (str) -> System.out.println(str));
    }


4.1.2 jdk中消费型接口测试


代码示例:Consumer接口


在JDK1.8中Collection集合接口的父接口Iterable接口中增加了一个默认方法:


public default void forEach(Consumer<? super T> action)遍历Collection集合的每个元素,执行“xxx消费型”操作。


在JDK1.8中Map集合接口中增加了一个默认方法:


public default void forEach(BiConsumer<? super K,? super V> action)遍历Map集合的每对映射关系,执行“xxx消费型”操作。


案例:


(1)创建一个Collection系列的集合,添加你知道的编程语言,调用forEach方法遍历查看


(2)创建一个Map系列的集合,添加一些(key,value)键值对,例如,添加编程语言排名和语言名称,调用forEach方法遍历查看


29c6da66ee114fb487207acdcc123724.png


示例代码:

    @Test//jdk中消费型接口测试
    public void testJdkConsumer() {
//        创建一个List集合
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
//        遍历出集合的每一个元素
//        for (Integer integer : list) {
//            System.out.println(integer);
//        }
//        list.forEach(new Consumer<Integer>() {
//            @Override
//            public void accept(Integer integer) {
                integer就是遍历出的每一个元素,可以直接打印
//            }
//        });Jdk里边已经定义了foreach,直接调用即可
        list.forEach(i -> System.out.println(i));
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "C");
        map.put(3, "C++");
        map.put(4, "JavaScript");
        map.put(5, "c#");
        map.put(6, "Python");
        map.put(7, "PHP");
        map.put(8, "Matlab");
        map.put(9, "Go");
//        map.forEach(new BiConsumer<Integer, String>() {
//            @Override
//            public void accept(Integer integer, String s) {
//              System.out.println("第" + integer + "个,值为" + s);
//            }
//        });
        map.forEach((i, s) -> System.out.println("第" + i + "个,值为" + s));
    }


4.2 供给型接口

4.2.1 自定义供给型接口测试

定义接口:

package Jdk8.Lambda1;
import java.util.function.Supplier;
public class LambdaManager {
    public static void userSupplier(Supplier<String> supplier) {
        String res = supplier.get();
        System.out.println("调用供给型接口的值为" + res);
    }
}


接口测试:

    //供给型接口
    @Test
    public void testSupplier() {
//        LambdaManager.userSupplier(new Supplier<String>() {
//            @Override
//            public String get() {
//                return "xiaogao";
//            }
//        });//和下边相同
        LambdaManager.userSupplier(() -> "xiaogao");
    }


4.2.2 jdk中供给型接口测试


代码示例:Supplier接口


在JDK1.8中增加了StreamAPI,java.util.stream.Stream是一个数据流。这个类型有一个静态方法:


public static <T> Stream<T> generate(Supplier<T> s)可以创建Stream的对象。而又包含一个forEach方法可以遍历流中的元素:public void forEach(Consumer<? super T> action)。


案例:


现在请调用Stream的generate方法,来产生一个流对象,并调用Math.random()方法来产生数据,为Supplier函数式接口的形参赋值。最后调用forEach方法遍历流中的数据查看结果。


@Test
public void testJdkSupplier(){
    Stream.generate(() -> Math.random()).forEach(num -> System.out.println(num));
}


4.3 功能型接口


4.3.1 自定义功能型接口测试

定义接口:

package Jdk8.Lambda;
import java.util.function.Function;
public class LambdaManager {
   public static void userFunction(String str, Function<String, String> function) {
        String apply = function.apply(str);
        System.out.println("返回的结果是" + apply);
    }
}


接口测试:

   //    功能型接口
    @Test
    public void testFunction() {
//        LambdaManager.userFunction("ming", new Function<String, String>() {
//            @Override
//            public String apply(String s) {
//                return s += "你好";
//            }
//        });
        LambdaManager.userFunction("uzi", (s -> s += "你好啊"));
        //返回的结果是uzi你好啊
    }


4.3.2 jdk中功能型接口测试

代码示例:Funtion<T,R>接口


在JDK1.8时Map接口增加了很多方法,例如:

public default void replaceAll(BiFunction<? super K,? super V,? extends V> function)按照function指定的操作替换map中的value。


public default void forEach(BiConsumer<? super K,? super V> action)遍历Map集合的每对映射关系,执行“xxx消费型”操作。


 @Test//jdk中功能型接口测试
    public void testJdkFunction() {
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "Java");
        map.put(2, "C");
        map.put(3, "C++");
        map.put(4, "JavaScript");
        map.put(5, "c#");
        map.put(6, "Python");
        map.put(7, "PHP");
        map.put(8, "Matlab");
        map.put(9, "Go");
//        目标:将java学科的名字替换成JavaWeb
       /* for (Map.Entry<Integer, String> entry : map.entrySet()) {
            Integer key=entry.getKey();
            String value=entry.getValue();
            if (value.equals("Java")) {
                map.put(key,"JavaWeb");
            }
        }//常规写法*/
        /*map.replaceAll(new BiFunction<Integer, String, String>() {
            @Override
            public String apply(Integer k, String v) {
//                判断v是否为java
                if (v.equals("Java")) {
                    return "JavaWeb";
                }
                return v;
            }
        });//匿名内部类写法*/
        map.replaceAll((k, v) -> {
            if (v.equals("Java")) {
                return "JavaWeb";
            }
            return v;
        });//Lambda表达式写法
        map.forEach((i, s) -> System.out.println("第" + i + "个,值为" + s));//使用foreach打印
    }


4.4 断言型接口


4.4.1 自定义断言型接口测试

定义接口:

package Jdk8.Lambda;
import java.util.function.Predicate;
public class LambdaManager {
    public static void userPredicate(String name, Predicate<String> predicate) {
        boolean flag = predicate.test(name);
        System.out.println(flag);
    }
}


接口测试:

 //断言型接口
    @Test
    public void testPredicate() {
//        LambdaManager.userPredicate("uzi", new Predicate<String>() {
//            @Override
//            public boolean test(String s) {
//                return s.equals("ming");
//            }
//        });
        LambdaManager.userPredicate("ming", (s -> s.equals("ming")));
    }


4.4.2 jdk中断言型接口测试


代码示例:Predicate接口

JDK1.8时,Collecton接口增加了一下方法,其中一个如下:

public default boolean removeIf(Predicate<? super E> filter) 用于删除集合中满足filter指定的条件判断的。


public default void forEach(Consumer<? super T> action)遍历Collection集合的每个元素,执行“xxx消费型”操作。

  @Test
    public void testJdkPredicate() {
    /*
 1)添加一些字符串到一个Collection集合中
(2)调用forEach遍历集合
(3)调用removeIf方法,删除其中字符串的长度<5的
(4)再次调用forEach遍历集合*/
        ArrayList<String> list = new ArrayList<>();
        list.add("Java");
        list.add("C");
        list.add("Python");
        list.add("PHP");
        list.add("JavaScript");
        //调用forEach遍历集合
        list.forEach(s -> System.out.println(s));
        //调用removeIf方法,删除其中字符串的长度<5的
        list.removeIf(s -> {
            if (s.length() < 5) {
                return true;
            }
            return false;
        });
        System.out.println("--------------");
        //再次调用forEach遍历集合
        list.forEach(s -> System.out.println(s));
    }



4.5 案例


(1)声明一个Employee员工类型,包含编号、姓名、性别,年龄,薪资。


(2)声明一个EmployeeSerice员工管理类,包含一个ArrayList集合的属性all,在EmployeeSerice的构造器中,创建一些员工对象,为all集合初始化。


(3)在EmployeeSerice员工管理类中,声明一个方法:ArrayList get(Predicate p),即将满足p指定的条件的员工,添加到一个新的ArrayList 集合中返回。


(4)在测试类中创建EmployeeSerice员工管理类的对象,并调用get方法,分别获取:


   所有员工对象

   所有年龄超过35的员工

   所有薪资高于15000的女员工

   所有编号是偶数的员工

   名字是“张三”的员工

   年龄超过25,薪资低于10000的男员工


示例代码:


Employee类:

package com.itheima.pojo;
public class Employee{
  private int id;
  private String name;
  private char gender;
  private int age;
  private double salary;
  public Employee(int id, String name, char gender, int age, double salary) {
    super();
    this.id = id;
    this.name = name;
    this.gender = gender;
    this.age = age;
    this.salary = salary;
  }
  public char getGender() {
    return gender;
  }
  public void setGender(char gender) {
    this.gender = gender;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  public Employee() {
    super();
  }
  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public double getSalary() {
    return salary;
  }
  public void setSalary(double salary) {
    this.salary = salary;
  }
  @Override
  public String toString() {
    return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", age=" + age + ", salary=" + salary
        + "]";
  }
}


员工管理类:

class EmployeeService{
  private ArrayList<Employee> all;
  public EmployeeService(){
    all = new ArrayList<Employee>();
    all.add(new Employee(1, "张三", '男', 33, 8000));
    all.add(new Employee(2, "翠花", '女', 23, 18000));
    all.add(new Employee(3, "无能", '男', 46, 8000));
    all.add(new Employee(4, "李四", '女', 23, 9000));
    all.add(new Employee(5, "老王", '男', 23, 15000));
    all.add(new Employee(6, "大嘴", '男', 23, 11000));
  }
  public ArrayList<Employee> get(Predicate<Employee> p){
    ArrayList<Employee> result = new ArrayList<Employee>();
    for (Employee emp : all) {
      if(p.test(emp)){
        result.add(emp);
      }
    }
    return result;
  }
}


测试类:

public class TestLambda {
  public static void main(String[] args) {
        //所有年龄超过35的员工
    EmployeeManager employeeManager = new EmployeeManager();
        List<Employee> employeeList1 = employeeManager.find(e -> e.getAge() > 35);
        employeeList1.forEach(e -> System.out.println(e));
        //所有薪资高于15000的女员工
        List<Employee> employeeList2 = employeeManager.find(e -> e.getSalary() > 15000 && e.getGender() == '女');
        employeeList2.forEach(e -> System.out.println(e));
        //所有编号是偶数的员工
        List<Employee> employeeList3 = employeeManager.find(e -> e.getId() % 2 == 0);
        employeeList2.forEach(e -> System.out.println(e));
        //名字是“张三”的员工
        List<Employee> employeeList4 = employeeManager.find(e -> e.getName() == "张三");
        employeeList4.forEach(e -> System.out.println(e));
        //年龄超过25,薪资低于10000的男员工
        List<Employee> employeeList5 = employeeManager.find(e -> e.getAge() > 25 && e.getSalary() < 10000 && e.getGender() == '男');
        employeeList5.forEach(e -> System.out.println(e));
  }
}


5 方法引用与构造器引用


Lambda表达式是可以简化函数式接口的变量与形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。当Lambda表达式满足一些特殊的情况时,还可以再简化:


(1)Lambda体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的

例如:System.out对象,调用println()方法来完成Lambda体

Math类,调用random()静态方法来完成Lambda体


(2)并且Lambda表达式的形参正好是给该方法的实参

例如:t->System.out.println(t)

() -> Math.random() 都是无参



5.1 方法引用

方法引用的语法格式:


(1)实例对象名::实例方法

(2)类名::静态方法

(3)类名::实例方法


说明:

   ::称为方法引用操作符(两个:中间不能有空格,而且必须英文状态下半角输入)

   Lambda表达式的形参列表,全部在Lambda体中使用上了,要么是作为调用方法的对象,要么是作为方法的实参。

   在整个Lambda体中没有额外的数据。

  @Test
  public void test1(){
//    Runnable r = () -> System.out.println("hello lambda");
    Runnable r = System.out::println;//打印空行
    //不能简化方法引用,因为"hello lambda"这个无法省略
  }
  @Test
  public void test2(){
    String[] arr = {"Hello","java","chai"};
//    Arrays.sort(arr, (s1,s2) -> s1.compareToIgnoreCase(s2));
    //用方法引用简化
    /*
     * Lambda表达式的形参,第一个(例如:s1),正好是调用方法的对象,剩下的形参(例如:s2)正好是给这个方法的实参
     */
    Arrays.sort(arr, String::compareToIgnoreCase);
  }
  @Test
  public void test3(){
//    Stream<Double> stream = Stream.generate(() -> Math.random());
    //用方法引用简化
    Stream<Double> stream = Stream.generate(Math::random);
  }
  @Test
  public void test4(){
    List<Integer> list = Arrays.asList(1,3,4,8,9);
    //list.forEach(t -> System.out.println(t));
    //用方法再简化
    list.forEach(System.out::println);
  }



5.2 构造器引用


(1)当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表。


(2) 当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度


构造器引用的语法格式:

   类名::new

   数组类型名::new

示例代码:


public class TestMethodReference {
    @Test
  public void test1() {
    Stream<Integer> stream = Stream.of(1,2,3);
    Stream<int[]> stream2 = stream.map(int[]::new);
  }    
    @Test
  public void test2() {
    Stream<String> stream = Stream.of("1.0","2.3","4.4");
//    Stream<BigDecimal> stream2 = stream.map(num -> new BigDecimal(num));
    Stream<BigDecimal> stream2 = stream.map(BigDecimal::new);
  }
  @Test
  public void test3(){
//    Supplier<String> s = () -> new String();//通过供给型接口,提供一个空字符串对象
    //构造器引用
    Supplier<String> s = String::new;//通过供给型接口,提供一个空字符串对象
  }
}
相关文章
|
程序员 Swift 开发者
26 函数式编程
函数式编程
65 0
|
2月前
|
机器学习/深度学习 数据采集 人工智能
函数式编程的实际应用
【10月更文挑战第12天】 函数式编程作为一种编程范式,在数据处理、金融、科学计算、Web 开发、游戏开发、物联网、人工智能等多个领域有着广泛应用。本文通过具体案例,详细介绍了函数式编程在这些领域的实际应用,展示了其在提高效率、确保准确性、增强可维护性等方面的显著优势。
144 60
|
1月前
|
数据采集 并行计算 算法
函数式编程
函数式编程是一种编程范式,它将计算视为数学函数的求值,并避免改变状态和可变数据。其核心思想是使用纯函数,减少副作用,提高代码的可读性和并行处理能力。
|
1月前
|
SQL 前端开发 测试技术
对函数式编程的深入理解
【10月更文挑战第25天】函数式编程提供了一种不同的编程思维方式,具有诸多优点,如提高代码质量、便于并发和并行编程、易于测试等。然而,它也存在一些局限性,需要根据具体的项目需求和场景来选择是否采用。随着对函数式编程的理解和应用的深入,它在现代软件开发中扮演着越来越重要的角色,为开发者提供了更多的编程选择和可能性。
17 1
|
4月前
|
自然语言处理 并行计算 大数据
什么是函数式编程
【8月更文挑战第2天】什么是函数式编程
125 13
|
5月前
|
Java 开发者
深入理解Java中的Lambda表达式与函数式编程
在Java 8中引入的Lambda表达式,为Java带来了一种全新的编程范式——函数式编程。本文将深入探讨Lambda表达式的概念、语法和实际应用场景,以及它如何改变我们编写和维护代码的方式。通过具体的例子,我们将看到Lambda表达式如何简化代码结构,提高开发效率,并使代码更加简洁易读。最后,我们将讨论Lambda表达式在多线程编程中的应用,以及它对Java开发者技能集的影响。
|
安全 Java 数据库
Lambda表达式和函数式编程
Lambda表达式和函数式编程
193 4
Lambda表达式和函数式编程
|
并行计算 JavaScript 数据可视化
快速了解函数式编程
快速了解函数式编程
138 0
快速了解函数式编程
|
Scala 索引 Python
第5章 函数式编程
第5章 函数式编程
521 0
第5章 函数式编程
|
存储 SQL 分布式计算
深入理解函数式编程
深入理解函数式编程
深入理解函数式编程