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<T>
—消费型接口代表了一个接受单个输入参数并且不返回任何结果的操作。
常用于执行某些操作,比如打印日志、发送消息等。
import java.util.function.Consumer; public class ConsumerExample { public static void main(String[] args) { // 使用Lambda表达式创建Consumer实例 Consumer<String> printer = s -> System.out.println(s); // 调用accept方法执行操作 printer.accept("Hello, Consumer!"); } }
-
Supplier<T>
—供给型接口代表了一个供应者,它不接受任何参数,并返回单个结果。
常用于生成或检索单个值。
import java.util.function.Supplier; public class SupplierExample { public static void main(String[] args) { // 使用Lambda表达式创建Supplier实例,该实例每次调用get方法时返回相同的值 Supplier<Integer> supplier = () -> 42; // 调用get方法获取值 System.out.println(supplier.get()); // 输出: 42 } }
-
Function<T,R>
—函数型接口接受一个输入参数T,并返回一个结果R。
下面是
Function
接口示例:import java.util.function.Function; public class FunctionExample { public static void main(String[] args) { // 使用Lambda表达式创建Function实例,将String转换为大写String Function<String, String> toUpperCase = s -> s.toUpperCase(); // 调用apply方法执行转换 String original = "hello, world!"; String transformed = toUpperCase.apply(original); // 输出转换后的结果 System.out.println(transformed); // 输出: HELLO, WORLD! } }
-
Predicate<T>
—断言型接口代表一个断言型接口,它接受一个输入参数T,并返回一个布尔值结果。
常用于条件判断。
import java.util.function.Predicate; public class PredicateExample { public static void main(String[] args) { // 使用Lambda表达式创建Predicate实例,检查字符串是否为空 Predicate<String> 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<Student> 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<T> {
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<Student> 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<Student> 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<T> {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
//使用了Student类构造方法引用创建了supplier实例,以后通过supplier.get()就可以获取一个Student类型的对象,前提是Student类中存在无参构造方法。
Supplier<Student> supplier = Student::new;
接下来我们将真正开始学习stream ,请看下篇: