跳到主要内容

第17章-4-函数式编程

概述

  • 从Java 8中引入
  • 能大大提高编程效率
  • 针对集合类,应用广泛

函数式接口

  • 函数式接口要求

    • 一个接口有且只有一个抽象方法
    • 建议在类上加上@FunctionalInterface注解,但不是必需
  • 定义实例,定义了一个算术运算的函数式接口

    package com.bjpowernode.demo;

    /**
    * 运算类
    */
    @FunctionalInterface
    public interface IOperation {
    /**
    * 算术运算
    * @param first
    * @param second
    * @return
    */
    Integer operate(Integer first,Integer second);
    }

Lambda表达式

  • 针对函数式接口进行应用

  • 匿名类的一个语法糖,效果一样,但代码更加简洁

  • 通过箭头语法(->)实现,更直观

  • 规则如下

    • 语法一:标准版,格式为:(参数列表)->{代码片断}
    • 语法二:简版,一般用于只有一行代码的场景,格式为:(参数列表)->简单表达式;另外,如果参数列表只有一个参数,可省略括号
  • 应用实例,多种函数式编程方式

    • 实例1,匿名类方式

      实例代码

      package com.bjpowernode.demo;

      /**
      * 算术运算实例,使用IOperation的匿名类方式
      */
      public class OperationDemo {
      public static void main(String[] args) {
      //定义匿名类,进行加法运算
      IOperation operation = new IOperation() {
      @Override
      public Integer operate(Integer first, Integer second) {
      return first + second;
      }
      };

      //使用匿名类的加法运算
      System.out.println(operation.operate(33,44));
      }
      }

      输出结果:77

    • 实例2,Lambda普通方式

      实例代码

      package com.bjpowernode.demo;

      /**
      * 算术运算实例,使用IOperation的Lambda方式
      */
      public class OperationDemo {
      public static void main(String[] args) {
      //定义Lambda,进行加法运算
      IOperation operation = (Integer first, Integer second) -> {
      return first + second;
      };

      //使用匿名类的加法运算
      System.out.println(operation.operate(33, 44));
      }
      }

      输出结果:77

    • 实例3,Lambda简写方式

      实例代码

      package com.bjpowernode.demo;

      /**
      * 算术运算实例,使用IOperation的Lambda简写方式,将进行类型推断
      */
      public class OperationDemo {
      public static void main(String[] args) {
      //定义Lambda简写方式,进行加法运算,会进行【类型推断】,只接收IOperation.operate定义的形式参数必须为Integer
      IOperation operation = (first, second) -> first + second;

      //使用匿名类的加法运算
      System.out.println(operation.operate(33, 44));
      // System.out.println(operation.operate(33.00,44.00)); //错误,类型不匹配
      }
      }

      输出结果:77

  • 类型推断,上述的方式3中,形式参数first、second和返回值之所以能不定义数据类型,因因为这Lambda表达式赋值给了IOperation接口引用,此接口有唯一的抽象方法operation,此时的Lambda表达式签名可以直接推断first、second的为Integer类型;此即类型推断

【练习】

  1. 练习应用实例内容,完成代码编写
  2. 编写程序,针对Long类型数据进行操作,可以是找最大值、最小值、平均值等,要求如下:
    1. 定义函数式接口,参数为Long类型的数组,返回值为Long类型数据
    2. 使用Lambda,利用函数式接口进行操作,分别返回一组测试数据的最大值、最小值、平均值和随机值

Lambda常见使用方式

概述

  • 在实际的开发过程中,会有一些常用的方法模式
  • 针对这些常见的方法模式,都可以定义函数式接口进行简化使用

有参无返回值-->Consumer<T>

  • 应用实例

    • 应用实例1

      • 函数式接口

        package com.bjpoernode.demo;

        /**
        * 【函数式接口】有参无返回值
        */
        @FunctionalInterface
        public interface IConsumer<T> {
        /**
        * 抽象方法
        * @param t 参数
        */
        void accept(T t);
        }
      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Consumer;

        /**
        * 【函数式接口】有参无返回值测试
        */
        public class ConsumerTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        IConsumer <Integer> consumer = new IConsumer <Integer>() {
        @Override
        public void accept(Integer data) {
        System.out.println("参数是:" + data);
        }
        };
        consumer.accept(5);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        IConsumer <Integer> lambda = data -> System.out.println("参数是:" + data);
        lambda.accept(5);
        }
        }

无参有返回值-->Supplier<T>

  • 应用实例

    • 应用实例1

      • 函数式接口

        package com.bjpoernode.demo;

        /**
        * 【函数式接口】无参有返回值
        */
        @FunctionalInterface
        public interface ISupplier<T> {
        /**
        * 抽象方法
        *
        * @return 返回值
        */
        T get();
        }
      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Consumer;
        import java.util.function.Supplier;

        /**
        * 【函数式接口】无参有返回值测试
        */
        public class SupplierTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        ISupplier <String> supplier = new ISupplier <String>() {
        @Override
        public String get() {
        return "我是最棒的!";
        }
        };
        String supplierResult = supplier.get();
        System.out.println(supplierResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        ISupplier <String> lambda = () -> "我是最棒的!";
        String lambdaResult = lambda.get();
        System.out.println(lambdaResult);
        }
        }

有参有返回值-->Function<T,R>

  • 应用实例

    • 应用实例1

      • 函数式接口

        package com.bjpoernode.demo;

        /**
        * 【函数式接口】有参有返回值
        */
        @FunctionalInterface
        public interface IFunction<T, R> {
        /**
        * 抽象方法
        *
        * @param t 参数
        * @return 返回值
        */
        R apply(T t);
        }
      • 函数式接口使用

        package com.bjpoernode.demo;

        /**
        * 【函数式接口】有参有返回值测试
        */
        public class FunctionTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        IFunction <Long, String> function = new IFunction <Long, String>() {
        @Override
        public String apply(Long data) {
        return data.toString();
        }
        };
        String functionResult = function.apply(1024l);
        System.out.println(functionResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        IFunction <Long, String> lambda = data -> data.toString();
        String lambdaResult = lambda.apply(1024l);
        System.out.println(lambdaResult);
        }
        }

有参有布尔型返回值-->Predicate<T>

  • 应用实例

    • 应用实例1

      • 函数式接口

        package com.bjpoernode.demo;

        /**
        * 【函数式接口】有参有布尔型返回值
        */
        public interface IPredicate<T> {
        /**
        * 抽象方法
        *
        * @param t 参数
        * @return 返回值
        */
        boolean test(T t);
        }
      • 函数式接口使用

        package com.bjpoernode.demo;

        /**
        * 【函数式接口】有参有布尔型返回值测试
        */
        public class PredicateTest {
        public static void main(String[] args) {
        System.out.println("-------------------匿名类使用方式-------------------");
        IPredicate <Integer> predicate = new IPredicate <Integer>() {
        @Override
        public boolean test(Integer data) {
        return data > 1024;
        }
        };
        boolean predicateResult = predicate.test(1025);
        System.out.println("是否大于1024?" + predicateResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        IPredicate <Integer> lambda = data -> data >= 1024;
        boolean lambdaResult = lambda.test(1025);
        System.out.println("是否大于1024?" + lambdaResult);
        }
        }

【练习】

  1. 练习应用实例内容,完成代码编写

常用的函数式接口

概述

  • 针对上一节Lambda常见使用方式的几个接口,由于在Java中经常使用,Java类库中已经提供了这些常用的函数式接口

  • 这些函数式接口多位于java.util.function.*包下

  • 具体见下表

    名字接口名对应的抽象方法
    消费型接口Consumer<T>void accept(T t);
    供给型接口Supplier<T>T get();
    函数式接口Function<T, R>R apply(T t);
    判断型接口Predicate<T>boolean test(T t);

有参无返回值-->Consumer<T>

  • 应用实例

    • 应用实例1

      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Consumer;

        /**
        * 【函数式接口】有参无返回值测试
        */
        public class ConsumerTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        Consumer <Integer> consumer = new Consumer <Integer>() {
        @Override
        public void accept(Integer data) {
        System.out.println("参数是:" + data);
        }
        };
        consumer.accept(5);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Consumer <Integer> lambda = data -> System.out.println("参数是:" + data);
        lambda.accept(5);
        }
        }

无参有返回值-->Supplier<T>

  • 应用实例

    • 应用实例1

      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Supplier;

        /**
        * 【函数式接口】无参有返回值测试
        */
        public class SupplierTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        Supplier <String> supplier = new Supplier <String>() {
        @Override
        public String get() {
        return "我是最棒的!";
        }
        };
        String supplierResult = supplier.get();
        System.out.println(supplierResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Supplier <String> lambda = () -> "我是最棒的!";
        String lambdaResult = lambda.get();
        System.out.println(lambdaResult);
        }
        }

有参有返回值-->Function<T,R>

  • 应用实例

    • 应用实例1

      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Function;

        /**
        * 【函数式接口】有参有返回值测试
        */
        public class FunctionTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        Function <Long, String> function = new Function <Long, String>() {
        @Override
        public String apply(Long data) {
        return data.toString();
        }
        };
        String functionResult = function.apply(1024l);
        System.out.println(functionResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Function <Long, String> lambda = data -> data.toString();
        String lambdaResult = lambda.apply(1024l);
        System.out.println(lambdaResult);
        }
        }

有参有布尔型返回值-->Predicate<T>

  • 应用实例

    • 应用实例1

      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Predicate;

        /**
        * 【函数式接口】有参有布尔型返回值测试
        */
        public class PredicateTest {
        public static void main(String[] args) {
        System.out.println("-------------------匿名类使用方式-------------------");
        Predicate <Integer> predicate = new Predicate <Integer>() {
        @Override
        public boolean test(Integer data) {
        return data > 1024;
        }
        };
        boolean predicateResult = predicate.test(1025);
        System.out.println("是否大于1024?" + predicateResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Predicate <Integer> lambda = data -> data >= 1024;
        boolean lambdaResult = lambda.test(1025);
        System.out.println("是否大于1024?" + lambdaResult);
        }
        }

【练习】

  1. 练习应用实例内容,完成代码编写

方法引用【扩展】

概述

  • 让简版Lambda更简单
  • 常见的针对构造方法、普通方法和静态方法,语法规则如下
    • 普通方法:对象::普通方法
    • 静态方法:类::静态方法
    • 构造方法:类::new
    • 数组:数组::new
  • 具体逻辑是:任意类的方法签名,与函数式接口的方法签名**相同或一致**,就可以将该任意类的方法赋值给函数式接口的引用,并使用该类方法的逻辑;其他事情JVM会处理

针对常用的函数式接口的简化

有参无返回值-->Consumer<T>
  • 应用实例

    • 应用实例1

      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Consumer;

        /**
        * 【函数式接口】有参无返回值测试
        */
        public class ConsumerTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        Consumer <Integer> consumer = new Consumer <Integer>() {
        @Override
        public void accept(Integer data) {
        System.out.println("参数是:" + data);
        }
        };
        consumer.accept(5);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Consumer <Integer> lambda = data -> System.out.println("参数是:" + data);
        lambda.accept(5);

        //3、方法引用使用方式
        System.out.println("-------------------方法引用使用方式-------------------");
        Consumer <Integer> methodReference = ConsumerTest::accept;
        methodReference.accept(5);
        }

        /**
        * 有参无返回值方法
        *
        * @param data
        */
        public static void accept(Integer data) {
        System.out.println("参数是:" + data);
        }
        }
无参有返回值-->Supplier<T>
  • 应用实例

    • 应用实例1

      • 函数接口使用

        package com.bjpoernode.demo;

        import java.util.function.Consumer;
        import java.util.function.Supplier;

        /**
        * 【函数式接口】无参有返回值测试
        */
        public class SupplierTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        Supplier <String> supplier = new Supplier <String>() {
        @Override
        public String get() {
        return "我是最棒的!";
        }
        };
        String supplierResult = supplier.get();
        System.out.println(supplierResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Supplier <String> lambda = () -> "我是最棒的!";
        String lambdaResult = lambda.get();
        System.out.println(lambdaResult);

        //3、方法引用使用方式
        System.out.println("-------------------方法引用使用方式-------------------");
        Supplier <String> methodReference = SupplierTest::get;
        String methodReferenceResult = methodReference.get();
        System.out.println(methodReferenceResult);
        }

        /**
        * 无参有返回值方法
        *
        * @return
        */
        public static String get() {
        return "我是最棒的!";
        }
        }
有参有返回值-->Function<T,R>
  • 应用实例

    • 应用实例1

      • 函数函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Function;

        /**
        * 【函数式接口】有参有返回值测试
        */
        public class FunctionTest {
        public static void main(String[] args) {
        //1、匿名类使用方式
        System.out.println("-------------------匿名类使用方式-------------------");
        Function <Long, String> function = new Function <Long, String>() {
        @Override
        public String apply(Long data) {
        return data.toString();
        }
        };
        String functionResult = function.apply(1024l);
        System.out.println(functionResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Function <Long, String> lambda = data -> data.toString();
        String lambdaResult = lambda.apply(1024l);
        System.out.println(lambdaResult);

        //3、方法引用使用方式
        System.out.println("-------------------方法引用使用方式-------------------");
        Function <Long, String> methodReference = FunctionTest::apply;
        String methodReferenceResult = methodReference.apply(1024l);
        System.out.println(methodReferenceResult);
        }

        /**
        * 有参有返回值
        *
        * @param data 参数
        * @return 返回值
        */
        public static String apply(Long data) {
        return data.toString();
        }
        }
有参有布尔型返回值-->Predicate<T>
  • 应用实例

    • 应用实例1

      • 函数式接口使用

        package com.bjpoernode.demo;

        import java.util.function.Predicate;

        /**
        * 【函数式接口】有参有布尔型返回值测试
        */
        public class PredicateTest {
        public static void main(String[] args) {
        System.out.println("-------------------匿名类使用方式-------------------");
        Predicate <Integer> predicate = new Predicate <Integer>() {
        @Override
        public boolean test(Integer data) {
        return data > 1024;
        }
        };
        boolean predicateResult = predicate.test(1025);
        System.out.println("是否大于1024?" + predicateResult);

        //2、Lambda使用方式
        System.out.println("-------------------Lambda使用方式-------------------");
        Predicate <Integer> lambda = data -> data >= 1024;
        boolean lambdaResult = lambda.test(1025);
        System.out.println("是否大于1024?" + lambdaResult);

        //3、方法引用使用方式
        System.out.println("-------------------方法引用使用方式-------------------");
        Predicate <Integer> methodReference = PredicateTest::test;
        boolean methodReferenceResult = methodReference.test(1025);
        System.out.println("是否大于1024?" + methodReferenceResult);
        }

        /**
        * 有参有布尔型返回值测试
        *
        * @param data 参数
        * @return 返回值
        */
        public static boolean test(Integer data) {
        return data > 1024;
        }
        }

【练习】

  1. 练习应用实例内容,完成代码编写

应用实例

  • 应用实例1

    • 目标类

      package com.bjpoernode.demo;

      /**
      * 方法引用目标类
      */
      public class Target {
      private String name;

      /**
      * 普通方法(实例方法)
      *
      * @param data 参数
      * @return 返回值
      */
      public String normalMethod(Long data) {
      String result = "【普通方法】" + data.toString();

      return result;
      }

      /**
      * 静态方法
      *
      * @param data 参数
      * @return 返回值
      */
      public static String staticMethod(Long data) {
      String result = "【静态方法】" + data.toString();

      return result;
      }

      /**
      * 无参构造
      */
      public Target() {
      }

      /**
      * 有参构造
      *
      * @param name 参数
      */
      public Target(String name) {
      this.name = name;
      }

      public String getName() {
      return name;
      }

      public void setName(String name) {
      this.name = name;
      }

      @Override
      public String toString() {
      return "Target{" + "name='" + name + '\'' + '}';
      }
      }
    • 方法引用使用

      package com.bjpoernode.demo;

      import java.util.function.Function;

      /**
      * 【方法引用】使用
      */
      public class FunctionTest {
      public static void main(String[] args) {
      //1、匿名类使用方式
      System.out.println("-------------------匿名类使用方式-------------------");
      Function <Long, String> function = new Function <Long, String>() {
      @Override
      public String apply(Long data) {
      return data.toString();
      }
      };
      String functionResult = function.apply(1024l);
      System.out.println(functionResult);

      //2、Lambda使用方式
      System.out.println("-------------------Lambda使用方式-------------------");
      Function <Long, String> lambda = data -> data.toString();
      String lambdaResult = lambda.apply(1024l);
      System.out.println(lambdaResult);

      //3.1、方法引用使用方式-->【普通方法】
      System.out.println("-------------------方法引用使用方式-->【普通方法】-------------------");
      Target target = new Target();
      //函数式接口引用指向【具有相同签名的普通方法】,就能使用其逻辑
      Function <Long, String> methodReference1 = target::normalMethod;
      String methodReference1Result = methodReference1.apply(1024l);
      System.out.println(methodReference1Result);

      //3.2、方法引用使用方式-->【静态方法】
      System.out.println("-------------------方法引用使用方式-->【静态方法】-------------------");
      //函数式接口引用指向【具有相同签名的静态方法】,就能使用其逻辑
      Function <Long, String> methodReference2 = Target::staticMethod;
      String methodReference2Result = methodReference2.apply(1024l);
      System.out.println(methodReference2Result);

      //3.3、方法引用使用方式-->【构造方法】
      System.out.println("-------------------方法引用使用方式-->【构造方法】-------------------");
      //函数式接口引用指向【具有一致签名的构造方法】,就能使用其逻辑,返回一个通过构造方法创建的对象
      Function <String, Target> methodReference3 = Target::new;
      Target targetResult = methodReference3.apply("张三");
      System.out.println(targetResult);

      //3.4、方法引用使用方式-->【数组】
      System.out.println("-------------------方法引用使用方式-->【数组】-------------------");
      Function <Integer, int[]> methodReference4 = int[]::new;
      int[] arrayResults = methodReference4.apply(5);
      System.out.println("数组长度为:" + arrayResults.length);
      }
      }

【练习】

  1. 练习应用实例内容,完成代码编写