函数式接口和方法引用

函数式接口和方法引用

1. 方法引用

1.1 方法引用符

双冒号::为方法引用符,而它所在的表达式称为方法引用。如果Lambda表达式赋值的方法已经在某个类中有具体的实现,那么则可以通过双冒号来引用该方法作为Lambda表达式的替代者。

示例

java 复制代码
public class ActorTest {
    
    public static void main(String[] args) {
        Actor actor = System.out::println;
        actor.perform("跳舞");
    }
}

Actor接口中的void perform(String item)方法在实现时用的public void println(String str)方法。lambda表达式可以根据实现的接口方法推导省略,方法引用也可以根据实现的接口方法进行推导省略。void perfoem(String item) 方法中带有一个字符串类型的参数,public void println(String str)方法来实现时就可以接受这个字符串数组。

方法引用与lambda表达式一样,只能用于函数式接口。方法有静态方法、成员方法和构造方法之分,方法引用因此也可以分为静态方法引用、成员方法引用和构造方法引用

1.2 静态方法引用

语法

java 复制代码
类名::方法名

示例

java 复制代码
package com.lullaby._static;

public interface Calculator {

    int calculator(int a, int b);
}
java 复制代码
package com.lullaby._static;

public class MathUtil {

    public static int add(int a, int b) {
        return a + b;
    }

    public static int minus(int a, int b) {
        return a - b;
    }

    public static int multiply(int a, int b) {
        return a * b;
    }

    public static int divided(int a, int b) {
        return a / b;
    }
}
java 复制代码
package com.lullaby._static;

public class CalculatorTest {

    public static void main(String[] args) {
//        Calculator calculator = new Calculator() {
//            @Override
//            public int calculator(int a, int b) {
//                return 0;
//            }
//        };

//        Calculator calculator = (a, b) -> MathUtil.minus(a, b);
        Calculator calculator = MathUtil::minus;
        int result = calculator.calculator(1, 10);
        System.out.println(result);
        
        Calculator calculator1 = MathUtil::add;
        int result1 = calculator1.calculator(1, 10);
        System.out.println(result1);

    }
}

1.3 成员方法引用

语法

java 复制代码
对象名::方法名

示例

java 复制代码
package com.lullaby.member;

public interface Printable {

    void print(String info);
}
java 复制代码
package com.lullaby.member;

public class Printer {

    public void print(String info) {
        System.out.println(info);
    }
}
java 复制代码
package com.lullaby.member;

public class Computer {

    private Printer printer;

    public Computer(Printer printer) {
        this.printer = printer;
    }

    public void print(String info) {
//        Printable printable = new Printable() {
//            @Override
//            public void print(String info) {
//                   printer.print(info);
//            }
//        };
//        printable.print(info);

//        Printable printable = information -> this.printer.print(information);

        Printable printable = printer::print;
        printable.print(info);

    }
}
java 复制代码
package com.lullaby.member;

public class ComputerTest {

    public static void main(String[] args) {
        Computer computer = new Computer(new Printer());
        computer.print("This is method reference");
    }
}

注意:如果函数式接口的抽象方法中只有一个引用数据类型的参数,且实现过程中只需要调用该类型中定义的成员方法,那么可以使用静态引用的方式直接引用该成员方法

示例

java 复制代码
package com.lullaby.member;

public interface Actor {

    void perform(Person p);
}
java 复制代码
package com.lullaby.member;

public class Person {

    public void sing() {
        System.out.println("唱歌");
    }

    public void dance() {
        System.out.println("跳舞");
    }
}
java 复制代码
package com.lullaby.member;

public class ActorTest {
    public static void main(String[] args) {
//        Actor actor = new Actor() {
//            @Override
//            public void perform(Person p) {
//                p.sing();
//                p.dance();
//            }
//        };

//        Actor actor = p -> {
//            p.sing();
//            p.dance();
//        };

        Actor actor = Person::dance;
        Actor actor1 = Person::sing;
        actor.perform(new Person());
        actor1.perform(new Person());
    }
}

1.4 this引用成员方法

语法

java 复制代码
this::方法名

示例

java 复制代码
package com.lullaby.member;

public interface Camera {

    public void takePhoto(String name);
}
java 复制代码
package com.lullaby.member;

public class People {

    public void takePhoto(String name) {
        System.out.println("给" + name + "拍照");
    }

//    public void travel(String name) {
//        Camera c = new Camera() {
//            @Override
//            public void takePhoto(String name) {
//                People.this.takePhoto(name);
//            }
//        };
//    }

//    public void travel(String name) {
//        Camera c = str -> this.takePhoto(str);
//    }

    public void travel(String name) {
        Camera c = this::takePhoto;
        c.takePhoto(name);
    }
}
java 复制代码
package com.lullaby.member;

public class PeopleTest {

    public static void main(String[] args) {
        People people = new People();
        people.travel("金字塔");
    }
}

1.5 super引用成员方法

语法

java 复制代码
super::方法名

示例

java 复制代码
package com.lullaby.member;

public interface Customer {

    void communicateBusyness();
}
java 复制代码
package com.lullaby.member;

public class SoftEngineer {

    public void analysisBusyness() {
        System.out.println("分析业务");
    }
}
java 复制代码
package com.lullaby.member;

public class JavaProgrammer extends SoftEngineer{

    public void communicateWithCustomer() {
//        Customer customer = new Customer() {
//            @Override
//            public void communicateBusyness() {
//                JavaProgrammer.super.analysisBusyness();
//            }
//        };

//        Customer customer = () -> super.analysisBusyness();

        Customer customer = super::analysisBusyness;
        customer.communicateBusyness();
    }
}
java 复制代码
package com.lullaby.member;

public class JavaProgramTest {
    public static void main(String[] args) {
        JavaProgrammer javaProgrammer = new JavaProgrammer();
        javaProgrammer.communicateWithCustomer();
    }
}

1.6 构造方法引用

语法

java 复制代码
类名::new

示例

java 复制代码
package com.lullaby.constructor;

public class Student {

    private String name;

    private String sex;

    public Student(String name, String sex) {
        this.name = name;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                '}';
    }
}
java 复制代码
package com.lullaby.constructor;

public interface StudentBuilder {

    Student build(String name, String sex);
}
java 复制代码
package com.lullaby.constructor;

public class StudentBuilderTest {

    public static void main(String[] args) {
//        StudentBuilder studentBuilder = new StudentBuilder() {
//            @Override
//            public Student build(String name, String sex) {
//                return new Student(name, sex);
//            }
//        };

//        StudentBuilder studentBuilder = (name, sex) -> new Student(name, sex);

        StudentBuilder studentBuilder = Student::new;
        Student student = studentBuilder.build("张山", "男");
        System.out.println(student);
    }
}

2. 函数式接口

2.1 什么是函数式接口

函数式接口是仅包含一种抽象方法的任何接口。(一个函数式接口可能包含一个或多个默认方法或静态方法。)由于一个函数式接口仅包含一个抽象方法,因此在实现该方法时可以省略该方法的名称。

示例

java 复制代码
public interface Hello {
    void sayHello(String msg);
    
    default void show() {
        System.out.println("show");
    }
    
    static void print() {
        System.out.println("print");
    }
}

JDK8专门为函数式接口提供了一个注释标识@FunctionalInterface,该注释只能使用在接口类型的定义上,表明这是一个函数式接口,编译器在编译时就会对该接口进行检测:接口中是否只有一个抽象方法。如果有多个抽象接口方法或者一个抽象接口方法都没有,则将会编译错误

示例

java 复制代码
package com.lullaby.functional;

@FunctionalInterface
public interface Hello {

    void sayHello(String name);

    static void show(){};

    default void print(){};

    private void test() {};
}

注意:如果接口类型上没有@FunctionalInterface注解,但接口中只有一个抽象方法,这个接口也是函数式接口。这与@Override注解一样,即使方法上面没有写,同样是属于方法重写。

2.2 函数式编程

函数式编程是一种编程方式,在Java中,简单来说就是一个变量存储一个函数。而能实现这种赋值操作的只有lambda表达式

示例

java 复制代码
package com.lullaby.functional;

public class HelloTest {

    public static void main(String[] args) {

//        Hello hello = name -> System.out.println(name);
        Hello hello = System.out::println;
        hello.sayHello("蔡徐坤");
    }
}

2.3 lambd表达式延迟执行

应用场景

在某种条件下才会处理数据

示例

java 复制代码
package com.lullaby.lambda.lazy;

public interface MsgBuilder {
    String builderMsg(String ... infos);
}
java 复制代码
package com.lullaby.lambda.lazy;

public class PrintUtil {

    public static void print(boolean valid, String msg) {
        if (valid) {
            System.out.println(msg);
        }
    }

    private static String build(String...infos) {
        StringBuilder builder = new StringBuilder();
        for (String info : infos) {
            builder.append(info);
        }
        return builder.toString();
    }

    public static void print(boolean valid, String... infos) {
        if (valid) {
//            MsgBuilder msgBuilder = new MsgBuilder() {
//                @Override
//                public String builderMsg(String... infos) {
//                    return PrintUtil.build(infos);
//                }
//            };

            MsgBuilder msgBuilder = PrintUtil::build;
            System.out.println(msgBuilder.builderMsg(infos));
        }
    }

}
java 复制代码
package com.lullaby.lambda.lazy;

public class PrintTest {

    public static void main(String[] args) {
        String name = "zhangsan";
        String desc = " is friendly";
        // 不会打印任何信息,但是此时已经完成了字符串的组装,这是属于性能上的浪费
        PrintUtil.print(false, name + desc);
        // 不会打印任何信息,也不会构建字符串
        PrintUtil.print(false, name, desc);
        // 会打印信息时才会构建字符串
        PrintUtil.print(true, name, desc);

    }
}

2.4 Consumer接口

java 复制代码
void accept(T t);	// 接收一个被消费的数据

Consumer顾名思义就是消费者的意思。可以消费一个被接收到的数据,至于如何消费,就需要看这个接口被如何实现。

示例

java 复制代码
package com.lullaby.consumer;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Consumer;

public class ConsumerTest {

    public static void main(String[] args) {
        Consumer<String> consumer = System.out::println;
        consumer.accept("这是被消费的信息");

//        Consumer<String> consumer1 = new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s.charAt(0));
//            }
//        };
        Consumer<String> consumer1 = s -> System.out.println(s.charAt(0));
        consumer1.accept("This is a consumer");

        Consumer<String> consumer2 = consumer.andThen(consumer1);
        consumer2.accept("先打印在取第一个字符");
        // 将数组转换为集合
        List<Integer> number = Arrays.asList(1,2,3,4,5);
//        number.forEach(new Consumer<Integer>() {
//            @Override
//            public void accept(Integer integer) {
//                System.out.print(integer);
//            }
//        });
//        number.forEach(integer -> System.out.print(integer));
        number.forEach(System.out::println);

        Set<String> names = new HashSet<>();
        names.add("admin");
        names.add("test");
        names.add("developer");
//        names.forEach(str -> System.out.println(str));
        names.forEach(System.out::println);
    }
}

2.5 BigConsumer接口

java 复制代码
void accept(T t, U u);	// 接收两个被消费的数据

BiConsumer也是一个消费者,只是这个消费者可以一次性消费两个数据(一般是键值对)。至于如何消费,就需要看这个接口被如何实现

示例

java 复制代码
package com.lullaby.consumer;

import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;

public class BiConsumerTest {
    public static void main(String[] args) {
        BiConsumer<String, Integer> biConsumer = (str, i) -> System.out.println(str + "->" + i);
        biConsumer.accept("a", 1);

        Map<String, String> counties = new HashMap<>();
        counties.put("CN", "中国");
        counties.put("US", "美国");
        counties.put("EN", "英国");
        counties.forEach((s1, s2) -> System.out.println(s1 + " -> " + s2));
    }
}

2.6 Predicate接口

java 复制代码
boolean test(T t);	// 检测是否满足条件
default Predicate<T> and(Predicate<? super T> other);	// 条件之间的逻辑与衔接
default Predicate<T> negate();	// 条件取反
default Predicate<T> or(Predicate<? super T> other);	// 条件之间的逻辑或衔接

Predicate是条件的意思,可以检测给定数据是否满足条件,也可以与其他条件进行衔接。至于如何检测,就需要看这个接口被如何实现

示例

java 复制代码
package com.lullaby.Predicate;

import java.util.function.Predicate;

public class PredicateTest {
    public static void main(String[] args) {
//        Predicate<String> p1 = new Predicate<String>() {
//            @Override
//            public boolean test(String s) {
//                return s.startsWith("H");
//            }
//        };

        Predicate<String> p1 = str -> str.startsWith("H");
        System.out.println(p1.test("Hello"));
        System.out.println(p1.test("hello"));

        Predicate<String> p2 = str -> str.contains("Hello World");
        Predicate<String> p3 =  p1.negate();
        System.out.println(p3.test("Hello"));

        Predicate<String> p4 = p1.and(p2);
        System.out.println(p4.test("Hello World"));
        System.out.println(p4.test("Hello my World"));
        
        Predicate<String> p5 = p1.or(p2);
        System.out.println(p5.test("Hello my World"));
    }
}

练习

学生有姓名、性别和年龄,现有一个集合内存储有10名学生信息,请找出其中性别为男、年龄在20岁以上的学生,并在控制台进行输出

java 复制代码
package com.lullaby.Predicate;

public class Student {

    private String name;

    private String sex;

    private int age;


    public Student() {
    }

    public Student(String name, String sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return sex
     */
    public String getSex() {
        return sex;
    }

    /**
     * 设置
     * @param sex
     */
    public void setSex(String sex) {
        this.sex = sex;
    }

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

    /**
     * 设置
     * @param age
     */
    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "Student{name = " + name + ", sex = " + sex + ", age = " + age + "}";
    }
}
java 复制代码
package com.lullaby.Predicate;

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Exercise {

    public static void main(String[] args) {
        List<Student> list = Arrays.asList(
                new Student("张三", "男", 20),
                new Student("李四", "女", 21),
                new Student("王五", "男", 22),
                new Student("二狗", "女", 23),
                new Student("一狗", "男", 24),
                new Student("二狗", "女", 18),
                new Student("三狗", "男", 16),
                new Student("四狗", "女", 19),
                new Student("五狗", "男", 23),
                new Student("六狗", "女", 10)
        );

        Predicate<Student> p1 = student -> student.getSex().equals("男");
        Predicate<Student> p2 = student -> student.getAge() > 20;
        Predicate<Student> p3 = p1.and(p2);
        list.forEach(student -> {
            if (p3.test(student)) {
                System.out.println(student);
            }
        });
    }
}

2.7 Function接口

java 复制代码
R apply(T t);	// 将一个对象转换为另一种数据类型的对象
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after);	// 复合转换

Function 是功能的意思,可以将一种数据类型的对象转换为另一种数据类型的对象,至于如何转换,就需要看这个接口被如何实现。

示例

java 复制代码
package com.lullaby.function;

import java.util.function.Function;

public class FunctionTest {

    public static void main(String[] args) {
//        Function<String, Integer> f1 = new Function<String, Integer>() {
//            @Override
//            public Integer apply(String s) {
//                return Integer.parseInt(s);
//            }
//        };

//        Function<String, Integer> f1 = s -> Integer.parseInt(s);

        Function<String, Integer> f1 = Integer::parseInt;
        Integer i = f1.apply("123");
        System.out.println(i);

        Function<Integer, Double> f2 = integer -> integer * 10.0;
        System.out.println(f2.apply(i));

        Function<String, Double> f3 = f1.andThen(f2);
        Double d = f3.apply("5");
        System.out.println(d);
    }
}

练习

现有文本存储学生信息如下:

tex 复制代码
谢霆锋,男,37
刘德华,男,52
郭富城,男,46
张学友,男,40

要求将学生信息从文本中读取出来并转换为学生对象,然后存储在集合中

java 复制代码
package com.lullaby.function;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;

public class Exercise {

    private static class Student {

        private String name;

        private String sex;

        private int age;


        public Student() {
        }

        public Student(String name, String sex, int age) {
            this.name = name;
            this.sex = sex;
            this.age = age;
        }

        /**
         * 获取
         * @return name
         */
        public String getName() {
            return name;
        }

        /**
         * 设置
         * @param name
         */
        public void setName(String name) {
            this.name = name;
        }

        /**
         * 获取
         * @return sex
         */
        public String getSex() {
            return sex;
        }

        /**
         * 设置
         * @param sex
         */
        public void setSex(String sex) {
            this.sex = sex;
        }

        /**
         * 获取
         * @return age
         */
        public int getAge() {
            return age;
        }

        /**
         * 设置
         * @param age
         */
        public void setAge(int age) {
            this.age = age;
        }

        public String toString() {
            return "Student{name = " + name + ", sex = " + sex + ", age = " + age + "}";
        }
    }

    public static void main(String[] args) {
        String path = "chapter22\\src\\com\\lullaby\\function\\stu";
        Function<String, Student> function = str -> {
            String[] strings = str.split(",");
            Function<String, Integer> stringIntegerFunction = Integer::parseInt;
            return new Student(strings[0], strings[1], stringIntegerFunction.apply(strings[2]));
        };

        List<Student> students = readStudent(path, function);
        students.forEach(System.out::println);
    }

    public static List<Student> readStudent(String path, Function<String, Student> function) {
        List<Student> students = new ArrayList<>();
        try (FileReader reader = new FileReader(path);
             BufferedReader bf = new BufferedReader(reader)){
            String line;
            while ((line = bf.readLine()) != null) {
                students.add(function.apply(line));
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        return students;
    }
}
相关推荐
Bat U2 小时前
JavaEE|多线程(二)
java·开发语言
_Evan_Yao2 小时前
RAG中的“Chunk”艺术:我试过10种切分策略后总结的结论
java·人工智能·后端·python·软件工程
skylijf2 小时前
2026 高项第 6 章 预测考点 + 练习题(共 12 题,做完稳拿分)
笔记·程序人生·其他·职场和发展·软件工程·团队开发·产品经理
今天你TLE了吗2 小时前
LLM到Agent&RAG——AI概念概述 第二章:提示词
人工智能·笔记·后端·学习
烤麻辣烫2 小时前
JS基础
开发语言·前端·javascript·学习
froginwe112 小时前
C++ 文件和流
开发语言
魂梦翩跹如雨2 小时前
数据库的“契约” —— 约束(Constrains)
java·数据库·mysql
Dxy12393102162 小时前
Python在图片上画矩形:从简单边框到复杂标注的全攻略
开发语言·python
独自破碎E3 小时前
面试官:你有用过Java的流式吗?比如说一个列表.stream这种,然后以流式去处理数据。
java·开发语言