JDK8 函数式编程

本贴最后更新于 1611 天前,其中的信息可能已经事过景迁

函数式编程

为什么需要函数式编程?

在一般的开发过程中我们的函数式编程都是通过匿名类进行传输函数。对于一个不会复用且不易命名或者不好放进某个具体的类中的临时方法。我们都可以使用匿名函数对象来进行处理。而 JDK8 对开发中比较常见的几种情况提供了一系列通用的函数对象。

四个主要的函数式对象
  • Consumer : 描述消费泛型 T 对象且无返回值的匿名函数。
  • Supplier : 描述有返回值为泛型 T 对象的匿名函数。
  • Function : 描述输入泛型 T,返回泛型 R 的匿名函数。
  • Predicate: 描述匿名的条件判断。
我们以订单以及订单中的商品举例来说明以下四种函数对象

订单类:

public class Order {
    private List<Item> itemList;
    public List<Item> getItemList() {
        return itemList;
    }
    public void setItemList(List<Item> itemList) {
        this.itemList = itemList;
    }
}

商品类:

public class Item {

    private String productName;

    private Integer price;

    public Item(){

    }

    public Item(String productName, Integer price) {

        this.productName = productName;
        this.price = price;

    }

    public String getProductName() {
        return productName;
    }

    public void setProductName(String productName) {
        this.productName = productName;
    }

    public Integer getPrice() {
        return price;
    }

    public void setPrice(Integer price) {
        this.price = price;
    }

    @Override
    public String toString() {
        return "Item{" +
                "productName='" + productName + '\'' +
                ", price=" + price +
                '}';
    }
}

为了方便生成测试订单,做了一个测试基类


public class AbstractTest {

    protected static Order getTestOrder() {

        Order order = new Order();

        List<Item> itemList = new ArrayList<>();

        Item item1 = new Item("键盘", 16);
        Item item2 = new Item("鼠标", 32);
        Item item3 = new Item("笔记本支架", 25);

        itemList.add(item1);
        itemList.add(item2);
        itemList.add(item3);

        order.setItemList(itemList);

        return order;

    }


}

Consumer

当你需要对某个泛型对象进行处理,并没有需要返回的值的话我们可以使用 Consumer 进行临时的处理。

以下我们通过消费一个订单,我们的处理逻辑是将订单中的所有商品都优惠 3 块钱。

public class ConsumerTest extends AbstractTest {


    /**
     * 调用accept方法接收order对象,具体操作交给匿名函数对象
     *
     * @param order
     * @param orderConsumer
     */
    public static void consume(Order order, Consumer<Item> orderConsumer) {

        List<Item> itemList = order.getItemList();

        if (itemList == null || itemList.isEmpty()) {
            return;
        }

        for (Item item : itemList) {
            orderConsumer.accept(item);
        }

    }


    public static void main(String[] args) {

        // 对订单中的商品进行优惠处理
        consume(getTestOrder(), item -> item.setPrice(item.getPrice() - 3));

    }


}

Consumer 对象还可以有 andThen()方法,继续传入 Consumer 对象进行链式处理。

Supplier

当我们需要返回特定泛型的匿名函数时,可以使用 Supplie 对象

在下面的例子中,我们通过 Supplier 中的逻辑返回每个商品的价格


public class SupplierTest extends AbstractTest{


    /**
     * 提取商品的价格
     * @param item
     * @param supplier
     * @return
     */
    public static Integer getPrice(Item item, Supplier<Integer> supplier) {

        return supplier.get();

    }


    public static void main(String[] args) {

        Order order = getTestOrder();

        List<Item> itemList = order.getItemList();

        if (itemList == null || itemList.isEmpty()) {
            return;
        }

        List<Integer> priceList = new ArrayList<>();

        for (Item item : itemList) {
            priceList.add(getPrice(item, () -> item.getPrice()));
        }


        System.out.println(priceList.toString());

    }


}

Function

当我们需要既对泛型参数进行处理,并返回另一个泛型参数时,可以使用 Funciton 这个函数对象。

在以下我们通过 Function 解析商品描述字符串并返回商品对象


public class FunctionTest extends AbstractTest {

    /**
     * 通过商品描述字符串解析并返回商品对象
     *
     * @param itemStr
     * @param function
     * @return
     */
    public static Item getItem(String itemStr, Function<String, Item> function) {

        return function.apply(itemStr);

    }


    public static void main(String[] args) {

        List<String> itemStrList = new ArrayList<>();

        itemStrList.add("苹果,22");
        itemStrList.add("香蕉,33");
        itemStrList.add("橘子,12");

        List<Item> itemList = new ArrayList<>();

        for (String itemStr : itemStrList) {

            itemList.add(getItem(itemStr, s -> {

                String[] split = itemStr.split(",");

                return new Item(split[0], Integer.valueOf(split[1]));

            }));

        }

        System.out.println(itemList);

    }

}

在 Stream 流当中 Function 经常用来将参数对象转换为特定对象进行返回

Predicate

当我们需要将特定的判断表达式通过函数对象进行传入的时候,可以使用 Predicate 对象。

以下我们通过 Predicate 对象筛选出价格大于 30 的商品

public class PredicateTest extends AbstractTest {


    public static void findExpensiveItem(Order order, Predicate<Item> predicate) {

        List<Item> itemList = order.getItemList();

        if (itemList == null || itemList.size() == 0) {
            return;
        }

        for (Item item : itemList) {

            if (predicate.test(item)) {
                System.out.println(item);
            }

        }

    }

    public static void main(String[] args) {
        // 找出价钱大于30的商品
        findExpensiveItem(getTestOrder(), item -> item.getPrice() > 30);

    }

}

笔者个人总结,如有错误恳请网友评论指正。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • wizardforcel

    问题多了去了。

    首先可以用 Lombok 的 @Data 注解来简化 Bean 的编写。

    @Data
    public class Order {
        private List<Item> itemList;
    }
    
    @Data
    public class Item {
        private String productName;
        private Integer price;
    }
    

    然后创建不为空 List 的话,可以用 List.of 工厂方法,Java 9 以下可以用 Guava 的 ImmutableList.of

    
    List<Item> itemList = List.of(
        new Item("键盘", 16),
        new Item("鼠标", 32),
        new Item("笔记本支架", 25)
    );
    

    然后用了函数式编程就不要用 for 循环了,配合 Stream 把它干掉吧。

    Consumer:

    
    getTestOrder().getItemList()
        .stream()
        .filter(it -> it != null && !it.isEmpty())
        .forEach(orderConsumer::accept);
    

    Supplier:

    List<Integer> priceList = getTestOrder()
        .getItemList()
        .stream()
        .filter(it -> it != null && !it.isEmpty())
        .map(Item::getPrice)
        .collect(Collectors.toList());
    

    Function:

    List<Item> itemList = List.of("苹果,22", "香蕉,33", "橘子,12")
        .stream()
        .map(s -> s.split(","))
        .map(spl -> new Item(spl[0], Inetger.parseInt(spl[1])))
        .collect(Collectors.toList());
    

    Predicate:

    getTestOrder().getItemList()
        .stream()
        .filter(it -> it.getPrice() > 30)
        .forEach(System.out::println);
    
    1 回复
    3 操作
    wizardforcel 在 2020-07-26 16:44:00 更新了该回帖
    wizardforcel 在 2020-07-26 16:39:17 更新了该回帖
    wizardforcel 在 2020-07-26 16:37:49 更新了该回帖
  • 其他回帖
  • valarchie

    感谢老哥评论指正。 因为是随便起的项目所以没引入 lombok。

    之前对于 Stream 我比较保守。 因为如果使用同事不熟悉的东西会导致维护成本变高。
    而且 Stream 也不好调试。

    最近开始尝试 Stream 看看!😄

    感谢老哥指点 😂 !

    1 操作
    valarchie 在 2020-07-26 23:16:08 更新了该回帖