美文网首页
Java8-函数式编程

Java8-函数式编程

作者: leileitang_blog | 来源:发表于2020-03-13 16:07 被阅读0次

    Java8-函数式编程

    为什么java8 的Lambda 这么受关注?

    Java8可以用Lambda表达式很简便的写出复杂的处理集合的逻辑。Lambda 可以理解为一种匿名函数的代替。使用它可以简化代码,提高开发效率。

    函数编程演化历程

    函数编程演化历程

    • 1.将业务逻辑直接写死在代码里。
    • 2.将单一维度的条件做为参数传入方法中。方法内根据参数进行业务逻辑实现。
    • 3.将多个维度的条件做为参数传入方法中。业务实现需要根据不同的参数处理不同逻辑。
    • 4.将业务逻辑封装为一个实体类,方法接受实体类为参数,方法内部调用实体类的处理逻辑。
    • 5.调用方法时不在创建实体类,而是使用匿名函数的形式替代。
    • 6.使用Lambda表达式替代匿名函数的形式,做为方法的参数。真正实现判断逻辑参数化传递。

    函数编程

    函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming),函数式编程,逻辑式编程,常见的面向对象编程是也是一种命令式编程。

    命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机的指令序列。而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式。

    函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。在函数式语言中,函数作为一等公民,可以在任何地方定义,在函数内或函数外,可以作为函数的参数和返回值,可以对函数进行组合。

    Lambda表达式

    Lambda表达式的两种形式

    • (parameters) -> expression

    • (parameters) ->{ statements; }
      以下是lambda表达式的重要特征:

    • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

    • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

    • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

    • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

    表达式实例

    // 1. 不需要参数
    () -> 5 ;
    () -> System.out.println("hello world");
      
    // 2. 接收一个参数
    x -> 2 * x ;
    () -> System.out.println("hello world");
    
    // 3. 没有参数逻辑复杂
    () -> {
        System.out.println("hello world1");
        System.out.println("hello world2");
    }
    // 3. 接受2个参数(数字)
    BinaryOperator<Long> functionAdd = (x,y)-> x + y ;
    Long result = functionAdd(1L,2L);
      
    // 4. 接收2个int型整数,显示声明参数类型
    (int x, int y) -> x + y  
      
    // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
    (String s) -> System.out.print(s)
    
    

    以上就是lambda的介绍。

    函数式接口介绍

    通常Lambda表达式是用在函数式接口上使用的。从Java8开始引入了函数式接口,其说明比较简单:函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。 java8引入@FunctionalInterface 注解声明该接口是一个函数式接口。

    语法定义

    @FunctionalInterface
    public interface ICollectionService {
        void test();
    }
    

    常用的函数式接口

    接口 参数 返回类型 描述
    Predicate<T> T boolean 用于判别一个对象
    Consumer<T> T void 用于接收一个对象进行处理但没有返回
    Function<T,R> T R 转换一个对象为不同类型的对象
    Supplier<T> None T 提供一个对象
    UnaryOperator<T> T T 接收对象并返回同类型的对象
    BinaryOperator<T> (T,T) T 接收两个同类型的对象,并返回一个原类型的对象

    Predicate

    java.util.function.Predicate<T> 接口定义了一个名叫 test 的抽象方法,它接受泛型 T 对象,并返回一个boolean值。在对类型 T进行断言判断时,可以使用这个接口。通常称为断言性接口。
    例如

    @FunctionalInterface
    public interface PredicateTest<T> {
        boolean test(T t);
    }
    
    @SpringBootTest
    class LambdaTestApplicationTests {
    
        //借助Lambda 表达式实现Predicate test方法
        @Test
        public void test3(){
            PredicateTest<String> predicateTest = (str)->str.isEmpty()||str.trim().isEmpty();
            System.out.println(predicateTest.test(""));
            System.out.println(predicateTest.test(" "));
            System.out.println(predicateTest.test(" as"));
        }
    }
    
    

    Consumer

    java.util.function.Consumer<T>接口定义了一个名叫 accept 的抽象方法,它接受泛型T,没有返回值(void)。如果需要访问类型 T 的对象,并对其执行某些操作,可以使用这个接口,通常称为消费性接口。

        @FunctionalInterface
        public interface ConsumerTest<T> {
            void accept(T t);
        }
    
      /**
         * 借助Lambda表达式实现Consumer accept方法
         */
        @Test
        public void test4(){
            ConsumerTest<Collection> consumerTest = collection ->{
                if (!CollectionUtils.isEmpty(collection)){
                    for (Object o : collection) {
                        System.out.println(o);
                    }
                }
            };
    
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
    
            consumerTest.accept(list);
        }
    

    Function

    java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象。如果需要定义一个Lambda,将输入的信息映射到输出,可以使用这个接口(比如提取苹果的重量,或把字符串映射为它的长度),通常称为功能性接口。

        @FunctionalInterface
        public interface FunctionTest<T,R> {
            R apply(T t);
        }
    
        @Test
        public void test5() {
            FunctionTest<String, String> functionTest = password -> Base64.getEncoder().encodeToString(password.getBytes());
            System.out.println(functionTest.apply("123456"));
        }
    

    Supplier

    java.util.function.Supplier<T>接口定义了一个get的抽象方法,它没有参数,返回一个泛型T的对象,这类似于一个工厂方法,通常称为功能性接口。

        @FunctionalInterface
        public interface SupplierTest<T> {
            T get();
        }
    
        @Test
        public void test6(){
            int[] arr = {100,0,-50,880,99,33,-30};
            SupplierTest<Integer> s = ()->{
                int max = arr[0];
                //遍历数组,获取数组中的其他元素
                for (int i : arr) {
                    //使用其他的元素和最大值比较
                    if(i>max){
                        //如果i大于max,则替换max作为最大值
                        max = i;
                    }
                }
                //返回最大值
                return max;
            };
           System.out.println(s.get().intValue());
        }
    

    UnaryOperator

    java.util.function.UnaryOperator<T> 定义了一个identity方法.
    这个接口,只接收一个泛型参数T,集成Function接口,也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数;这个接口定义了一个静态方法,返回泛型对象的本身;

        @Test
        public void test7() {
            UnaryOperator<Integer> test = x -> x + 100;
            System.out.println(test.apply(100));
        }
    

    BinaryOperator

    java.util.function.BinaryOperator<T>接口用于执行lambda表达式并返回一个T类型的返回值。

        /**
         * 接收两个同类型的对象,并返回一个原类型的对象
         */
        @Test
        public void test8(){
            BinaryOperator<Integer> add = (n1, n2) -> n1 + n2;
            //apply方法用于接收参数,并返回BinaryOperator中的Integer类型
            System.out.println(add.apply(3, 4));
        }
    

    BiConsumer

    这个接口接收两个泛型参数,跟Consumer一样,都有一个 accept方法,只不过,这里的,接收两个泛型参数,对这两个参数做下消费处理;使用这个函数式接口的终端操作有map的遍历;下面看下面的例子,两个参数消费数据的.可以看到,Map接口的终端操作,forEach的参数就是BiConsumer函数接口,对HashMap 的数据进行消费;BiConsumer函数接口还有一个默认函数,andThen,接收一个BiConsumer接口,先执行本接口的,再执行传入的参数。

    Map<String, String> map = new HashMap<>();
            map.put("a", "a");
            map.put("b", "b");
            map.put("c", "c");
            map.put("d", "d");
            map.forEach((k, v) -> {
                System.out.println(k);
                System.out.println(v);
            });
    

    方法引用

    有时候我们不是必须要自己重写某个匿名内部类的方法,我们可以可以利用 lambda表达式的接口快速指向一个已经被实现的方法。 调用特定方法的lambda表达式的一种快捷写法,可以让你重复使用现有的方法定义,并像lambda表达式一样传递他们。

    语法:
    方法归属者::方法名
    静态方法的归属者为类名,普通方法归属者为对象

    指向静态方法的方法引用

    public void test(){
         Consumer<String> consumer = (String s) -> Integer.parseInt(s);
            Consumer<String> consumer1 = Integer::parseInt;
    }
    

    指向对象的实例方法引用

    StringBuilder stringBuilder = new StringBuilder();
      Consumer<String> consumer = (String s) -> stringBuilder.append(s);
      Consumer<String> consumer1 = stringBuilder::append;
    

    相关文章

      网友评论

          本文标题:Java8-函数式编程

          本文链接:https://www.haomeiwen.com/subject/befrshtx.html