Java8-函数式编程

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 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 boolean 用于判别一个对象
Consumer T void 用于接收一个对象进行处理但没有返回
Function T R 转换一个对象为不同类型的对象
Supplier None T 提供一个对象
UnaryOperator T T 接收对象并返回同类型的对象
BinaryOperator (T,T) T 接收两个同类型的对象,并返回一个原类型的对象

Predicate

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

@FunctionalInterface
public interface PredicateTest {
    boolean test(T t);
}

@SpringBootTest
class LambdaTestApplicationTests {

    //借助Lambda 表达式实现Predicate test方法
    @Test
    public void test3(){
        PredicateTest 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接口定义了一个名叫 accept 的抽象方法,它接受泛型T,没有返回值(void)。如果需要访问类型 T 的对象,并对其执行某些操作,可以使用这个接口,通常称为消费性接口。

    @FunctionalInterface
    public interface ConsumerTest {
        void accept(T t);
    }

  /**
     * 借助Lambda表达式实现Consumer accept方法
     */
    @Test
    public void test4(){
        ConsumerTest consumerTest = collection ->{
            if (!CollectionUtils.isEmpty(collection)){
                for (Object o : collection) {
                    System.out.println(o);
                }
            }
        };

        List list = new ArrayList();
        list.add("1");
        list.add("2");
        list.add("3");

        consumerTest.accept(list);
    }

Function

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

    @FunctionalInterface
    public interface FunctionTest {
        R apply(T t);
    }

    @Test
    public void test5() {
        FunctionTest functionTest = password -> Base64.getEncoder().encodeToString(password.getBytes());
        System.out.println(functionTest.apply("123456"));
    }

Supplier

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

    @FunctionalInterface
    public interface SupplierTest {
        T get();
    }

    @Test
    public void test6(){
        int[] arr = {100,0,-50,880,99,33,-30};
        SupplierTest 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 定义了一个identity方法.
这个接口,只接收一个泛型参数T,集成Function接口,也就是说,传入泛型T类型的参数,调用apply后,返回也T类型的参数;这个接口定义了一个静态方法,返回泛型对象的本身;

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

BinaryOperator

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

    /**
     * 接收两个同类型的对象,并返回一个原类型的对象
     */
    @Test
    public void test8(){
        BinaryOperator 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 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 consumer = (String s) -> Integer.parseInt(s);
        Consumer consumer1 = Integer::parseInt;
}

指向对象的实例方法引用

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

你可能感兴趣的:(Java8-函数式编程)