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中四大函数式接口

  1. 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!");  
        }  
    }
    
  2. 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  
        }  
    }
    
  3. 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!  
        }  
    }
    
  4. 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中主要有四种类型的方法引用:
  1. 静态方法引用:通过类名来引用静态方法。

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

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

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

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

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

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

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

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

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

    语法:类名::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 ,请看下篇:

  • 33
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值