zoukankan      html  css  js  c++  java
  • Java8内置的函数式编程接口应用场景和方式

    首先,我们先定义一个函数式编程接口

    @FunctionalInterface
    public interface BooleanFunctionalInterface<T> {
        boolean test(T t);
    }

    很简单,该接口的唯一一个抽象方法(并且非Object类的方法)返回值为boolean

    下面,定义一个方法,接受一个List,利用实现了该接口的test方法的对象,筛选出需要的元素:

    import org.springframework.util.CollectionUtils;
    
    import java.util.ArrayList;
    import java.util.List;
    import java.util.function.Predicate;
    
    public class Filter<T> {
        public static <T> List<T> filter(List<T> list, BooleanFunctionalInterface b){
            if (CollectionUtils.isEmpty(list)){
                return new ArrayList<>(0);
            }
    
            List<T> result = new ArrayList<>(list.size());
            for(int i=0; i<list.size(); i++){
                T t = list.get(i);
                if (b.test(t)) {
                    result.add(t);
                }
            }
    
            return result;
        }
    }

     测试类,筛选出年龄大于25的People对象:

    public class FunctionalInterfaceTest {
        private List<People> peopleList = new ArrayList<>();
        @Before
        public void init(){
            peopleList.add(new People("LuoTianyan",23));
            peopleList.add(new People("ff",26));
            peopleList.add(new People("Tony",33));
        }
        /**
         * 自定义函数式接口
         */
        @Test
        public void testUserDefined(){
            List<People> filter = Filter.filter(peopleList, p -> ((People) p).getAge() > 25);
            filter.forEach(System.out::println);
            /*People(name=ff, age=26)
            People(name=Tony, age=33)*/
        }
    }
    import lombok.*;
    
    @Getter
    @Setter
    @ToString
    @AllArgsConstructor
    @NoArgsConstructor
    public class People {
        private String name;
        private int age;
    }

    JDK中已有的函数式接口

    上面的自定义的函数式接口,返回boolean,其实在Java8中已经有该类型的接口,那就是Predicate。

    Predicate<T> 接口

    该接口定义了一个支持泛型的boolean test( T)的抽象方法,其函数描述符为 (T)-> boolean,现在我们就可以直接使用Predicate<T>接口来替代上面自定义的接口了。

    在上面的Filter类中追加一个方法,修改这里形参为Predicate。

    public static <T> List<T> predicate(List<T> list, Predicate predicate){
            if (CollectionUtils.isEmpty(list)){
                return new ArrayList<>(0);
            }
    
            List<T> result = new ArrayList<>(list.size());
            for(int i=0; i<list.size(); i++){
                T t = list.get(i);
                if (predicate.test(t)) {
                    result.add(t);
                }
            }
    
            return result;
        }

    获取Age>25的People对象,测试如下:

    /**
         * Java8内置的函数式编程接口Predicate,返回boolean类型
         */
        @Test
        public void testPredicate(){
            List<People> predicate = Filter.predicate(peopleList, p -> ((People) p).getAge() > 25);
            predicate.forEach(System.out::println);
        }

    Consumer<T>接口

    该接口定义了一个void accept(T)的抽象方法,其函数描述符为 (T) -> void,如果你需要一个操作某一对象,但无需返回的的函数式接口,那么就可以使用Consumer<T>接口。

    追加方法,形参为Consumer:

    public static <T> void consumer(List<T> list, Consumer consumer){
            if (CollectionUtils.isEmpty(list)){
                return ;
            }
    
            List<T> result = new ArrayList<>(list.size());
            for(int i=0; i<list.size(); i++){
                T t = list.get(i);
                consumer.accept(t);
            }
    
        }

    下面实现修改所有Age为18,并且输出,测试如下: 

    /**
         * Java8内置的函数式编程接口Consumer,直接消费无返回值
         */
        @Test
        public void testConsumer(){
            // setAge操作不需要返回值
            Consumer<People> setAgeConsumer = p -> ((People) p).setAge(18);
            Filter.consumer(peopleList, setAgeConsumer);
    
            // 输出操作不需要返回值
            Consumer<People> sout = p -> System.out.println((People)p);
            Filter.consumer(peopleList, sout);
            /*People(name=LuoTianyan, age=18)
            People(name=ff, age=18)
            People(name=Tony, age=18)*/
        }

    Supplier<T>接口

    既然有消费者接口(Consumer<T>),那就要有生产者接口(Supplier<T>),该接口定义了一个 T get() 的抽象方法,其函数描述符为 () -> T,如果不接受入参,直接为我们生产一个指定的结果,那么就可以用Supplier<T>。

    追加方法,形参Supplier

    public static <T> List<T> listFactory(int count, Supplier<T> supplier){
    
            List<T> result = new ArrayList<>(count);
            for(int i=0; i<count; i++){
                T t = supplier.get();
                result.add(t);
            }
    
            return result;
        }

    下面生成count个对象,设置对象默认属性值:

    /**
         * Java8内置的函数式编程接口supplier,无形参,返回对象
         */
        @Test
        public void testSupplier(){
            // 生成对象
            Supplier<People> peopleSupplier = () -> new People("init",18);
            List<People> people = Filter.listFactory(5, peopleSupplier);
    
            // 输出操作不需要返回值
            Consumer<People> sout = p -> System.out.println((People)p);
            Filter.consumer(people, sout);
            /*People(name=init, age=18)
            People(name=init, age=18)
            People(name=init, age=18)
            People(name=init, age=18)
            People(name=init, age=18)*/
        }

    Function<T,R>接口

    该接口定义了一个 R apply(T)类型的抽象函数,它接受一个泛型变量T,并返回一个泛型变量R,如果你需要将一个对象T映射成R,那么就可以使用Function<T,R>接口。

    下面,我们将对象转化为String类型的例子

    public static <T> List<String> function(List<T> list, Function<T,String> function){
            if (CollectionUtils.isEmpty(list)){
                return new ArrayList<>(0);
            }
    
            List<String> result = new ArrayList<>(list.size());
            for(int i=0; i<list.size(); i++){
                T t = list.get(i);
                String apply = function.apply(t);
                result.add(apply);
            }
    
            return result;
        }

    将People对象,转换为的字符串输出:
    /**
         * Java8内置的函数式编程接口Function,接受形参T,转换为对象R
         */
        @Test
        public void testFunction(){
            // 将People对象,转换为如下形式的字符串
            Function<People, String> function = (People p) -> "name:" + p.getName() + "  , age:" + p.getAge();
            List<String> strings = Filter.function(peopleList, function);
    
            // 输出操作不需要返回值
            Consumer<String> sout = p -> System.out.println(p);
            Filter.consumer(strings, sout);
            /*name:LuoTianyan  , age:23
            name:ff  , age:26
            name:Tony  , age:33*/
        }

    上面代码的优化

    由于上面的people是list集合,可以直接利用stream的形式;

    比如People对象转换成字符串

    @Test
        public void testListStream(){
            List<String> collect = peopleList.stream()
                    .map(p -> p.getName() + p.getAge())
                    .collect(Collectors.toList());
    
            collect.forEach(System.out::println);
            /*LuoTianyan23
             ff26
            Tony33*/
        }

    Java 8 中函数式接口列表

    现在我们给出一份较为全的函数式接口与描述符对应的接口声明列表:

        函数式接口     函数描述符
    Predicate<T>   (T)  -> boolean
    Consumer<T>   (T)  -> void
    Function< T, R >   (T)  -> R
    Supplier<T>   ( )  -> T
    UnaryOperator<T>    (T)  ->  T
    BinaryOperator<T>   (T, T) -> T
    BiPredicate<L, R>   (L, R)  -> boolean
    BiConsumer<T, U>   (T, U)  -> void
    BiFunction<T, U, R>   (T, U)  -> R

     需要的函数式接口没有被覆盖,可以根据JDK中的声明来编写适合自己使用的函数式接口声明。

    BiFunction例子

    关于装箱与拆箱

    泛型的使用使得函数式接口有了更高的灵活性,我觉得这里应该先说一下参数化,参数化是相对于硬编码来说的 ,如我们常用的函数声明具有参数列表lambda表达式采用了代码参数化技术泛型则使用了类型参数化技术,参数化是代码走向通用的方法 ,同时也是编程抽象的一种体现。

     

    为了程序员的方便,JDK中提供了现成的支持泛型的函数式接口,但是由于泛型的支持,使得接口也会存在一些性能浪费的问题。我们知道Java泛型只能支持引用类型,也就是对象,不支持原始类型(int、double、char等),在 Java SE5之前Java程序员在泛型中使用原始类型时,只能通过其对应的引用类型(Interger、Double、Charactor)来替换,并且需要使用函数式式的方式进行原始类型到引用类型的转换,如 Integer i = new Integer(10),从 Java SE5开始,Java支持自动装箱拆箱技术,通过赋值操作,便可以将原始类型包装成引用类型如Integer i = 10,相对的自动拆箱便是将引用类型转为原始类型。

     

    但是这样的特性也会带来牺牲性能的代价,装箱的本质是将原始类型包裹起来生成一个对象出来,并将原始类型的值保存到该对象中,相对于原始类型包装的过程和内存的占用都会相应的提高,并且在很多情况下我们使用原始类型就足够了。为此,Java 8 提供了一批避免原始类型装箱的函数式接口。例如IntPredicate、IntConsumer、DoublePredicate、IntFunction等,使用原始类型作为接口命名的前缀便是对应的避免装箱的函数式接口声明。

    查看具体声明,读者应该可以发现,这些接口的函数描述符完全没变,只是泛型使用了具体的原始类型来替代,如下:

    来源:https://mp.weixin.qq.com/s?__biz=MzIzMzgxOTQ5NA==&mid=2247483845&idx=1&sn=08990fd78e4f62ddf38238660cc4dd64&chksm=e8fe9dccdf8914da580e13a9fc5fee64c135f55d11f3c2de5731f052fa9f1a39060ed6375110&scene=21#wechat_redirect

    Java8 BiFunction

  • 相关阅读:
    分期付款购买固定资产账务处理
    会计要素计量
    接受现金捐赠分录
    分配股票股利的分录
    R语言代写对用电负荷时间序列数据进行K-medoids聚类建模和GAM回归
    R语言代写用随机森林和文本挖掘提高航空公司客户满意度
    R语言代写时间序列TAR阈值模型分析 2
    R语言代写时间序列TAR阈值模型分析
    R语言代写文本挖掘tf-idf,主题建模,情感分析,n-gram建模研究
    R语言代写文本挖掘NASA数据网络分析,tf-idf和主题建模
  • 原文地址:https://www.cnblogs.com/theRhyme/p/10774720.html
Copyright © 2011-2022 走看看