Java 8 函数式编程基础:Lambda表达式与四大函数式接口以及方法引用详解

1 Lambda表达式

Lambda表达式是Java 8及更高版本中引入的一个关键特性,它提供了一种编写匿名函数(即没有名称的函数)的简洁方式。Lambda表达式可以传递给函数式接口(Functional Interface)的实例,函数式接口是只包含一个抽象方法的接口(可以包含多个默认方法或静态方法,但只能有一个抽象方法)。

1.1 Lambda表达式的基本语法如下:

(parameters) -> expression

(parameters) ->{ statements; }

parameters 是参数列表,expression 或 { statements; } 是Lambda 表达式的主体。如果只有一个参数,可以省略括号;如果没有参数,也需要空括号。

1.2 Lambda表达式写法:

使用一个->符号,箭头将Lambda表达式分为左右两部分,左边写的是实现的这个接口中的抽象方法中的形参列表,右边就是对抽象方法的处理;

1.3 Lambda 表达式的结构:

Lambda表达式不是万能的,他需要函数式接口的支持;@FunctionalInterface 注解,可以检查它是否是一个函数式接口;

1.3.1以RunnAble接口举例:

原本的使用方法:

//继承Runnable

public class MyRunAble implements Runnable{

@Override

public void run() {

System.out.println("-----");

}

}

}

//测试类

public class Test03 {

public static void main(String[] args) {

MyRunAble myRunAble=new MyRunAble();

Thread thread=new Thread(myRunAble);

thread.start()

}

}

//运行结果如下:

-----

Lambda表达式:

public static void main(String[] args) {

//lambda表达式

Runnable runnable1 = () -> {

System.out.println("-----");

};

Thread thread1=new Thread(runnable1);

thread1.start();

}

}

//运行结果如下:

-----

可以省略方法名,IDEA会帮你自动检测方法名;—上述省略的是runnable接口中run()方法

如果对抽象方法的实现逻辑只有一行,可以省略方法体的大括号,当然如果不止一行,就不能省略了;

//一行运行代码,{}可以省略

Runnable runnable2 = () -> System.out.println("-----");

1.3.2自定义接口举例:

interface test{

int fun(Integer a);

}

测试:

//一个参数时()可以省略

//一行运行代码,有返回值时,{}可以省略 ,同时return省略

test t = a -> a;

System.out.println(t.fun(5));

可以省略方法中的形参类型;形参列表中只有一个参数,可以去掉形参的括号;有返回值的方法,如果要去掉大括号,还需要去掉return关键字;

//Lambda表达式也可以作为参数传递

//此时匿名函数是Runnable

Thread thread = new Thread(() -> {

System.out.println("线程执行");

});

2 Java中四大函数式接口

Consumer—消费型接口

代表了一个接受单个输入参数并且不返回任何结果的操作。

常用于执行某些操作,比如打印日志、发送消息等。

import java.util.function.Consumer;

public class ConsumerExample {

public static void main(String[] args) {

// 使用Lambda表达式创建Consumer实例

Consumer printer = s -> System.out.println(s);

// 调用accept方法执行操作

printer.accept("Hello, Consumer!");

}

}

Supplier—供给型接口

代表了一个供应者,它不接受任何参数,并返回单个结果。

常用于生成或检索单个值。

import java.util.function.Supplier;

public class SupplierExample {

public static void main(String[] args) {

// 使用Lambda表达式创建Supplier实例,该实例每次调用get方法时返回相同的值

Supplier supplier = () -> 42;

// 调用get方法获取值

System.out.println(supplier.get()); // 输出: 42

}

}

Function—函数型接口

接受一个输入参数T,并返回一个结果R。

下面是Function接口示例:

import java.util.function.Function;

public class FunctionExample {

public static void main(String[] args) {

// 使用Lambda表达式创建Function实例,将String转换为大写String

Function toUpperCase = s -> s.toUpperCase();

// 调用apply方法执行转换

String original = "hello, world!";

String transformed = toUpperCase.apply(original);

// 输出转换后的结果

System.out.println(transformed); // 输出: HELLO, WORLD!

}

}

Predicate—断言型接口

代表一个断言型接口,它接受一个输入参数T,并返回一个布尔值结果。

常用于条件判断。

import java.util.function.Predicate;

public class PredicateExample {

public static void main(String[] args) {

// 使用Lambda表达式创建Predicate实例,检查字符串是否为空

Predicate isEmpty = s -> s.isEmpty();

// 调用test方法执行判断

System.out.println(isEmpty.test("")); // 输出: true

System.out.println(isEmpty.test("not empty")); // 输出: false

}

}

3 方法引用

方法引用其实是Lambda表达式的另一种写法,当要传递给Lambda体的操作,已经有实现的方法了,可以使用方法引用;方法引用通过方法的名字来指向一个方法。方法引用可以使语言的构造更紧凑简洁,减少冗余代码。方法引用使用一对冒号 :: 。

3.1 Java中主要有四种类型的方法引用:

静态方法引用:通过类名来引用静态方法。

语法:类名::静态方法名

示例:Integer::parseInt 引用Integer类的parseInt(String s)静态方法。

对象的实例方法引用:通过特定对象来引用实例方法。

语法:对象::实例方法名

注意:这种类型的方法引用不太常用,因为Lambda表达式通常用于传递无状态的操作,而特定对象的引用通常包含状态。

特定类型的任意对象的实例方法引用:通过类名来引用该类型任意对象的实例方法。Lambda表达式的第一个参数会成为调用该方法的对象。

语法:类名::实例方法名

示例:String::length 引用String类中任意对象的length()实例方法。

构造方法引用:通过类名来引用构造方法。

语法:类名::new

示例:ArrayList::new 引用ArrayList类的构造方法。

3.2 示例

第一种 类名::静态方法名

①自定义一个学生对象:两个属性name和score并提供了初始化name和score的构造方法,并且在最下方提供了两个静态方法分别按score和name进行比较先后顺序。

public class Student {

private String name;

private int score;

public Student(){

}

public Student(String name,int score){

this.name = name;

this.score = score;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public int getScore() {

return score;

}

public void setScore(int score) {

this.score = score;

}

public static int compareStudentByScore(Student student1,Student student2){

return student1.getScore() - student2.getScore();

}

public static int compareStudentByName(Student student1,Student student2){

return student1.getName().compareToIgnoreCase(student2.getName());

}

}

②按着分数由小到大排列并输出:

Student student1 = new Student("a",20);

Student student2 = new Student("b",70);

Student student3 = new Student("c",80);

Student student4 = new Student("d",90);

List students = Arrays.asList(student1,student2,student3,student4);

//先使用lambda表达式的方式进行处理

//sort方法接收一个Comparator函数式接口,接口中唯一的抽象方法compare接收两个参数返回一个int类型值

students.sort((o1, o2) -> o1.getScore() - o2.getScore());

students.forEach(student -> System.out.println(student.getScore()));

//Comparator接口定义

@FunctionalInterface

public interface Comparator {

int compare(T o1, T o2);

}

//上述Student类中定义的compareStudentByScore静态方法

public static int compareStudentByScore(Student student1,Student student2){

return student1.getScore() - student2.getScore();

}

//发现同样是接收两个参数返回一个int类型值,而且是对Student对象的分数进行比较,所以我们这里就可以 使用类名::静态方法名 方法引用替换lambda表达式

students.sort(Student::compareStudentByScore);

students.forEach(student -> System.out.println(student.getScore()));

第二种 对象::实例方法名

自定义一个StudentComparator类

public class StudentComparator{

public int compareStudentByScore(Student student1,Student student2){

return student2.getScore() - student1.getScore();

}

}

//发现该方法的定义满足Comparator接口的compare方法定义,所以这里可以直接使用 对象::实例方法名 的方式使用方法引用来替换lambda表达式

//Comparator c=(o1, o2) -> o1.getScore() - o2.getScore()

//students.sort((o1, o2) -> o1.getScore() - o2.getScore());

StudentComparator studentComparator = new StudentComparator();

students.sort(studentComparator::compareStudentByScore);

students.forEach(student -> System.out.println(student.getScore()));

第三种 类名::实例方法名

一定是lambda表达式所接收的第一个参数来调用实例方法,如果lambda表达式接收多个参数,其余的参数作为方法的参数传递进去。

//在Student类中加入一个方法

public int compareByScore(Student student){

return this.getScore() - student.getScore();

}

//Comparator c1=(o1, o2)-> o1.compareByScore(o2);

//students.sort((o1, o2)-> o1.compareByScore(o2));

//类名::实例方法名

students.sort(Student::compareByScore);

students.forEach(student -> System.out.println(student.getScore()));

第四种 类名::new

Supplier函数式接口的get方法,不接收参数有返回值,正好符合无参构造方法的定义

@FunctionalInterface

public interface Supplier {

/**

* Gets a result.

*

* @return a result

*/

T get();

}

//使用了Student类构造方法引用创建了supplier实例,以后通过supplier.get()就可以获取一个Student类型的对象,前提是Student类中存在无参构造方法。

Supplier supplier = Student::new;

接下来我们将真正开始学习stream ,请看下篇:

随便看看