Java基础函数式编程

本篇博文目录:

1.什么是函数式接口

只包含一个抽象方法的接口,称为函数式接口,也称为SAM接口,即Single Abstract Method interfaces,用@FunctionalInterface注解进行标注,常见的函数式接口如Comparator,Runnable,Callable。

例子:

@FunctionalInterface
interface A {
   public void test(); // 只能包含一个抽象方法
}

并且还可以使用泛型,如下:

@FunctionalInterface
interface A {
   public void test(T t); // 只能包含一个抽象方法
}

函数式接口不仅仅只定义一个抽象方法,还可以定义

  • 函数式接口里允许定义java.lang.Object里的public方法
  • 函数式接口里允许定义静态方法
  • 函数式接口里允许定义默认方法
  • 等等

下面以 Comparator函数式接口为例:

Java基础函数式编程_第1张图片

  • 函数式接口里允许定义java.lang.Object里的public方法

这里的 equals(Object obj)方法来自于java.lang.Object里的public方法boolean equals(Object obj);

Java基础函数式编程_第2张图片

  • 函数式接口里允许定义静态方法

Java基础函数式编程_第3张图片

  • 函数式接口里允许定义默认方法

Java基础函数式编程_第4张图片

2.函数式编程

函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。历史上研究函数式编程的理论是Lambda演算,所以我们经常把支持函数式编程的编码风格称为Lambda表达式,Java从JDK1.8开始支持这种风格 ( 针对函数式接口的一种简单写法 )。

(1) 使用Lambda表达式

例子:

@FunctionalInterface
interface A {
 
   public void test(int a); // 只能包含一个抽象方法
}

class TestB {
 
    public static void Test(Integer num, A  a) {
 
        a.test(num);
    }

}
  • 使用匿名内部类(以前)
// 使用匿名内部类
        TestB.Test(1, new A() {
 
            @Override
            public void test(int a) {
 
                System.out.println("【方式1】实现A接口的test方法,num="+a);
            }
        });
  • 使用Lambda表达式

在以前对于这种 函数式接口参数 我们使用匿名匿名内部类的方式传递变量过去,从Java 8开始,我们可以用Lambda表达式进行简化。

// 使用Lambda表达式
        TestB.Test(2,(int a)->{
 
            System.out.println("【方式2】实现A接口的test方法,num="+a);
        });

运行效果:

Java基础函数式编程_第5张图片

(2) Lambda表达式的进一步简化

Lambda表达式在特殊情况下还可以进一步简化,如下:

未简化前:

// 使用Lambda表达式
TestB.Test(2,(int a)->{
 
 System.out.println("【方式2】实现A接口的test方法,num="+a);
});

【简化1】参数的数据类型可以省略

TestB.Test(2,(a)->{
 
System.out.println("【方式2】实现A接口的test方法,num="+a);
}
);

【简化2】只有一个参数的时候可以省略()

TestB.Test(2,a->{
 
System.out.println("【方式2】实现A接口的test方法,num="+a);
}
);

【简化3】执行代码块中只有一条语句/只有一个return语句的时候可以省略{}

TestB.Test(2,a->
System.out.println("【方式2】实现A接口的test方法,num="+a)
);

(3) Java内置函数式接口

前文中的函数式接口都是我们自己定义的,其实Java官方给我提供了一些函数式接口,我们可以直接使用这些接口,而无需自己去定义函数式接口。

  • Java内置4大核心函数式接口

Java基础函数式编程_第6张图片

  • 其他接口

Java基础函数式编程_第7张图片

上文例子中的函数式接口对照上表来看的话,和消费性接口相对应,这里我们采用Consumer接口来试试看:

定义TestB_2类,采用 Consumer 函数式接口:

class TestB_2 {
 
    public static void Test(Integer num, Consumer  c) {
 
        c.accept(num);
    }
}

使用Lambda表达式进行调用:

TestB_2.Test(3,(a)-> System.out.println("【方式3】实现A接口的test方法,num="+a));

运行效果:

Java基础函数式编程_第8张图片

其实还可以进一步简化:

BiConsumer consumer = (a, b) -> System.out.println(a+""+b);
 consumer.accept("【方式4】实现A接口的test方法,num=",4);

运行效果:

Java基础函数式编程_第9张图片

3.方法引用

(1) 方法引用的简单使用

方法引用(Method References)是一种语法糖,它本质上就是 Lambda 表达式,我们知道Lambda表达式是函数式接口的实例,所以说方法引用也是函数式接口的实例,通过 类/对象::方法 进行引用。

例子:

@FunctionalInterface
interface A {
 
   public void test(int a); // 只能包含一个抽象方法
}


class TestB {
 
    public static void Test(String[] arrays, A  a) {
 
        for (int i = 0; i < arrays.length; i++) {
 
            System.out.println(arrays[i]);
        }
    }

}

在前文中我们是通过Lambda表达式进行简化:

// 使用Lambda表达式
TestB.Test(2,(int a)->{
 
System.out.println("【方式2】实现A接口的test方法,num="+a);
});

当存在如下类时,C类下的静态方法testC(int a)和函数式接口方法 test(int a);的形参和返回值类型保持一致时,就可以通过方法引用进行简化,如下:

class C{
 
    public static void testC(int a){
 
        System.out.println("【方式5】实现A接口的test方法,num="+a);
    }
}

使用方法引用进行简化:

// 使用引用
 TestB.Test(5,C::testC);

运行效果:

Java基础函数式编程_第10张图片

(2) 方法引用的分类

对于方法引用的使用,通常可以分为以下 4 种情况:

  1. 对象 :: 非静态方法:对象引用非静态方法,即实例方法;
  2. 类 :: 静态方法:类引用静态方法;
  3. 类 :: 非静态方法:类引用非静态方法;
  4. 类 :: new:构造方法引用。

分清楚实例方法,静态方法和构造方法

下面的equals(Object o)方法就是实例方法:

public final class Integer {
 
    boolean equals(Object o) {
 
        ...
    }
}

下面的parseInt(String s)方法就是静态方法:

public final class Integer {
 
    public static int parseInt(String s) {
 
        ...
    }
}

下面的方法就是构造方法:

public class A{
 
   public A(){
 
   }
}

情况1( 对象 :: 非静态方法 ):

例子:

Consumer consumer = System.out::println;
consumer.accept("【情况1】( 对象 :: 非静态方法 )方法引用");

对应的Lambda表达式如下:

Consumer consumer2 = m -> System.out.println(m);

运行效果:

Java基础函数式编程_第11张图片

System.out为对象:

Java基础函数式编程_第12张图片

println为实例方法:

Java基础函数式编程_第13张图片

情况2( 类 :: 静态方法 ):

例子:

Comparator cm= Integer::compare;
System.out.println("【情况2】( 对象 :: 静态方法 )方法引用,100和99做比较:"+cm.compare(100, 99));

Lambda表达式如下:

Comparator cm2 = (x, y) -> Integer.compare(x, y);

运行效果:

Java基础函数式编程_第14张图片

Integer为类:

Java基础函数式编程_第15张图片

compare为静态方法:

Java基础函数式编程_第16张图片

情况3( 类 :: 非静态方法 ):

例子:

Comparator cmStr = String::compareTo;
System.out.println("【情况3】( 对象 :: 非静态方法 )方法引用,字符串a和b做比较:"+cmStr.compare("a", "b"));

Lambda表达式如下:

Comparator cmStr2 = (s1, s2) -> s1.compareTo(s2);

运行效果:

Java基础函数式编程_第17张图片

String为类:

Java基础函数式编程_第18张图片

compareTo为实例方法:

Java基础函数式编程_第19张图片

分清楚compareTo()方法和compare()方法的区别:

  • compareTo(Object o)方法是java.lang.Comparable接口中的方法,当需要对某个类的对象进行排序时,该类需要实现Comparable接口的,必须重写public int compareTo(T o)方法。
  • compare(Object o1,Object o2)方法是java.util.Comparator接口的方法,它实际上用的是待比较对象的compareTo(Object o)方法。

备注:compareTo()方法和compare()方法的区别更加详细解释,请参照这篇博文: java中compareTo和compare方法之比较

根据上面的理解我们来分析一下"a"和"b"是怎么进行比较的:

根据上文中二者的区别,可知使用 String::compareTo 表示采用String的排序操作,而 cmStr.compare("a", "b") 表示调用 "a" 的 compareTo("b") 进行比较。

如下图所示:

Java基础函数式编程_第20张图片

"a" 从哪里来的呢?,其实这个 "a" 就是this本身,而 this.value = 'a' :

Java基础函数式编程_第21张图片

情况4( 类 :: new ):

例子:

class Student{
 
    private String name;
    
    public Student(String name) {
 
        this.name = name;
    }

    public String getName() {
 
        return name;
    }
}

方法引用:

Function function = Student::new;
Student zs = function.apply("张三");
System.out.println("【情况4】( 类 :: new )方法引用,获取学生姓名:"+zs.getName());

Lambda表达式如下:

Function function2 = (name) -> new Student(name);

运行效果:

Java基础函数式编程_第22张图片

Student为类:

Java基础函数式编程_第23张图片

Function 这里的String对应Student的构造方法的参数类型

Java基础函数式编程_第24张图片

对上文的说法进行验证( 确实如此 ):

Java基础函数式编程_第25张图片

你可能感兴趣的:(java,java,开发语言,jvm)