• Java函数式编程


    函数式编程

    函数式编程

    函数式编程是一种编程范式,以简单粗暴的方式来理解,可以把它理解成匿名函数的一种代替,也有人把它叫做箭头函数->。它将对象的行为,或者说方法进行参数化传递。

    函数式编程初探

    什么是函数式编程思维?

    演进案例

    函数式编程是一种编程思想,用一个案例来描述这种思想是怎么演进的。

    有一个商品类Sku,它包含了商品种类,商品价格等多个属性。有一个购物车的类CartService,其中cartSkuList是一个商品的集合,并提供一系列筛选商品的方法。

    商品和购物车类

    /**
     * 业务硬编码:
     * what: 固定商品的种类查找,比如找出购物车中所有电子产品
     * how bad: 一个种类一个方法?显然不可取
     *
     * @param cartSkuList
     * @return
     */
    public static List filterElectronicsSkus(
     List cartSkuList) {
    ​
     List result = new ArrayList();
     for (Sku sku: cartSkuList) {
     // 如果商品类型 等于 电子类
     if (SkuCategoryEnum.ELECTRONICS.
     equals(sku.getSkuCategory())) {
     result.add(sku);
     }
     }
     return result;
    }
    ​
    /**
     * 单一维度判断条件:
     * what:支持查找不同类型的上篇,根据传入商品类型参数,找出购物车中同种商品类型的商品列表
     * how bad:如果还要根据价格进行筛选呢?
     *
     * @param cartSkuList
     * @param category
     * @return
     */
    public static List filterSkusByCategory(
     List cartSkuList, SkuCategoryEnum category) {
    ​
     List result = new ArrayList();
     for (Sku sku: cartSkuList) {
     // 如果商品类型 等于 传入商品类型参数
     if (category.equals(sku.getSkuCategory())) {
     result.add(sku);
     }
     }
     return result;
    }
    ​
    /**
     * 多维度判断条件:
     * what:支持通过商品种类和价格筛选,支持通过商品类型或总价来过滤商品
     * how bad:条件再多呢?能不能再抽象一点
     * @param cartSkuList
     * @param category
     * @param totalPrice
     * @param categoryOrPrice - true: 根据商品类型,false: 根据商品总价
     * @return
     */
    public static List filterSkus(
     List cartSkuList, SkuCategoryEnum category,
     Double totalPrice, Boolean categoryOrPrice) {
    ​
     List result = new ArrayList();
     for (Sku sku: cartSkuList) {
    ​
     // 如果根据商品类型判断,sku类型与输入类型比较
     // 如果根据商品总价判断,sku总价与输入总价比较
     if (
     (categoryOrPrice
     && category.equals(sku.getSkuCategory())
     ||
     (!categoryOrPrice
     && sku.getTotalPrice() > totalPrice))) {
     result.add(sku);
     }
     }
     return result;
    }
    public interface SkuPredicate {
    ​
     /**
     * 选择判断标准
     * @param sku
     * @return
     */
     boolean test(Sku sku);
    ​
    }
    ​
    /**
     * 对Sku的总价是否超出2000作为判断标准
     */
    public class SkuTotalPricePredicate implements SkuPredicate {
     @Override
     public boolean test(Sku sku) {
     return sku.getTotalPrice() > 2000;
     }
    }
    ​
    @Test
    public void filterSkus() {
     List cartSkuList = CartService.getCartSkuList();
    ​
     // 过滤商品总价大于2000的商品
     List result = CartService.filterSkus(
     cartSkuList, new SkuTotalPricePredicate());
    ​
     System.out.println(JSON.toJSONString(
     result, true));
    }
    @Test
    public void filterSkus() {
     List cartSkuList = CartService.getCartSkuList();
    ​
     // 过滤商品单价大于1000的商品
     List result = CartService.filterSkus(
     cartSkuList, new SkuPredicate() {
     @Override
     public boolean test(Sku sku) {
     return sku.getSkuPrice() > 1000;
     }
     });
    ​
     System.out.println(JSON.toJSONString(
     result, true));
    }
    @Test
    public void filterSkus() {
     List cartSkuList = CartService.getCartSkuList();
    ​
     // 过滤商品单价大于1000的商品
     List result = CartService.filterSkus(
     cartSkuList,
     (Sku sku) -> sku.getSkuPrice() > 1000);
    ​
     System.out.println(JSON.toJSONString(
     result, true));
    }
    (parameters) -> expression # 简单逻辑不需要 {}
    (parameters) -> { statement; } # 复杂逻辑添加 {}
    () -> System.out.println("Hello World!");
    name -> System.out.println("Hello World" + name + "!");
    () -> {
     System.out.println("Hello World!");
     System.out.println("Hello World!");
    }
    BinaryOperator functionAdd = (x, y) -> x + y;
    Long result = functionAdd.apply(1L, 2L);
    BinaryOperator functionAdd = (Long x, Long y) -> x + y;
    Long result = functionAdd.apply(1L, 2L);

    函数式接口代表的一种契约, 一种对某个特定函数类型的契约。 在它出现的地方,实际期望一个符合契约要求的函数。 Lambda表达式不能脱离上下文而存在,它必须要有一个明确的目标类型,而这个目标类型就是某个函数式接口。

    Java 8函数式接口functional interface的秘密

    具体来说,Java中我们预期被@FunctionalInterface标记的接口是一个函数式接口。这里并不是说,没有@FunctionalInterface的就不是函数式接口,有@FunctionalInterface就是函数式接口。

    比如说Runnable接口在 Java8 之前就存在,它是一个函数式接口,在 Java8 中才加上 @FunctionalInterface。此外在 Java8 之前就有的函数式接口还有:

    • java.lang.Runnable

    • java.util.concurrent.Callable

    • java.security.PrivilegedAction

    • java.util.Comparator

    • java.io.FileFilter

    • java.nio.file.PathMatcher

    • java.lang.reflect.InvocationHandler

    • java.beans.PropertyChangeListener

    • java.awt.event.ActionListener

    • javax.swing.event.ChangeListener

    如果我们想要自己定义一个函数式接口,可以像下面这样:

    /**
     * 文件处理函数式接口
     */
    @FunctionalInterface
    public interface FileConsumer {
     /**
     * 函数式接口抽象方法
     * @param fileContent - 文件内容字符串
     */
     void fileHandler(String fileContent);
    }
    ​
    /**
     * 文件服务类
     */
    public class FileService {
    ​
     /**
     * 从过url获取本地文件内容,调用函数式接口处理
     * @param url
     * @param fileConsumer
     */
     public void fileHandle(String url, FileConsumer fileConsumer)
     throws IOException {
    ​
     // 若干操作...
     
     // 调用函数式接口方法,将文件内容传递给lambda表达式,实现业务逻辑
     fileConsumer.fileHandler(stringBuilder.toString());
     }
    }
    ​
    public class FileServiceTest {
     @Test
     public void fileHandle() throws IOException {
     FileService fileService = new FileService();
    ​
     // TODO 此处替换为本地文件的地址全路径
     String filePath = "";
    ​
     // 通过lambda表达式,打印文件内容
     fileService.fileHandle(filePath,
     fileContent -> System.out.println(fileContent)
     );
     }
    }
    /**
     * 三种方法引用
     */
    public class MethodReference {
    ​
     /**
     * 指向静态方法的方法引用
     *
     * (args) -> ClassName.staticMethod(args);
     * ClassName::staticMethod;
     */
     public void test1() {
     Consumer consumer1
     = (String number) -> Integer.parseInt(number);
    ​
     Consumer consumer2
     = Integer::parseInt;
     }
    ​
     /**
     * 指向任意类型实例方法的方法引用
     *
     * (args) -> args.instanceMethod();
     * ClassName::instanceMethod;
     */
     public void test2() {
     Consumer consumer1
     = (String str) -> str.length();
    ​
     Consumer consumer2
     = String::length;
     }
    ​
     /**
     * 指向现有对象的实例方法的方法引用
     *
     * (args) -> object.instanceMethod(args);
     * object::instanceMethod;
     */
     public void test3() {
     StringBuilder stringBuilder = new StringBuilder();
    ​
     Consumer consumer1
     = (String str) -> stringBuilder.append(str);
    ​
     Consumer consumer2
     = stringBuilder::append;
     }
    ​
    }
    ​

    方法引用是调用特定方法的Lambda表达式的一种快捷写法,可以让你重复使用现有的方法定义,并像Lambda表达式一样传递他们。

    方法引用

    Java定义好的函数式接口

    java.util.function中定义了几组类型的函数式接口以及针对基本数据类型的子接口。

    Java定义好的函数式接口

    函数式接口

    形式五:对参数显示声明

    形式四:包含两个参数的方法

    形式三:没有三处,逻辑复杂

    形式二:只有一个参数

    形式一:没有参数

    Lambda表达式主要有两种形式:

    Java8引入函数式编程风格,可以将其理解为一种匿名函数的代替,作用是将行为参数化传递,或者说将一个方法当成一个参数。

    Lambda表达式

    Java8之后,为我们提供了Lambda表达式。

    SkuPredicate接口只定义了一个test()方法,我们可以用匿名内部类:

    前面的代码展示了,我们将条件作为入参,让方法能够做到多条件的筛选。**那如果条件更多怎么办?**还有一种方法就是将判断逻辑进一步抽象成一个接口,接口中定义一个方法test()判断该商品是否符合筛选条件。

  • 相关阅读:
    Ubuntu 更换软件源
    Ubuntu 配置 SOCKS5
    Ubuntu 配置 Sha-dow-socks
    frp(内网穿透)
    solr 远程代码执行(CVE-2019-12409)
    多线程处理爬虫
    python实现文件自动排序
    python 实现根据文件名自动分类移动至不同的文件夹
    Centos7如何开启任意端口服务
    centos7默认安装没有连接网络
  • 原文地址:https://www.cnblogs.com/shuiyj/p/13185071.html
Copyright © 2020-2023  润新知