22、函数式接口和方法引用

1、方法引用

1.1 应用场景

官方文档说明

	使用lambda表达式创建匿名方法,有时除了调用现有方法外什么也不做,在这种情况下,通常按名称引用现有方法更容易。
	方法引用可以实现此操作,它们是紧凑的,x对于已经具有名称的方法lambda表达式更易于阅读。

即在匿名内部类仅调用一条已经在别的类实现了的现有方法时,用方法引用,使之更容易阅读。

示例

public class Example01 {

    public static void main(String[] args) {

        //函数式编程思想,用lambda语法实现
        Actor actor = s -> System.out.println(s);

        actor.performance("演员表演节目");
    }
}

interface Actor {
    void performance(String s);
}

分析

在上面的例子中,Lambda表达式的作用就是调用System.out对象中println(String msg)方法,而这个方法已经有具体的实现,如果能够直接引用这个方法,那么代码将会变得更简洁。

1.2 方法引用符

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

示例

public class Actor {
    public static void main(String args[]) {
        Actor actor = System.out::println;
        actor.performance("演员表演节目");
    }
}

interface Actor {
    void performance(String s);
}

分析

Actor接口中的performance(String msg)方法在实现时用System.out对象中println(String x)方法,Lambda表达式可以根据已经实现的接口方法推导省略,方法引用也可以根据实现的接口方法进行推导省略。

void performance(String item)方法中带有一个字符串类型的参数,public void println(String x)方法来实现时就可以接收这个字符串参数。

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

1.3 静态方法引用

语法

类名::方法名

1.4 成员方法引用

语法

对象名::方法名

示例

public class Computer {
    private Printer printer;

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

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

        //lambda表达式
//        Printable printable = message -> printer.print(msg);

        //方法引用
        Printable printable = printer::print;

        printable.print(msg);
    }
}

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

示例

public class ActorTest {

    public static void main(String[] args) {

        //lambda表达式
//        Actor actor = p -> p.dance();

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

public interface Actor {

    //函数式接口的抽象方法只有一个引用数据类型的参数
    void perform(Person p);
}

public class Person {

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

    public void dance() {
        System.out.println("跳舞");
    }
}

1.4.1 this引用成员方法

语法

this::方法名

示例

public class PersonTest {

    public static void main(String[] args) {

        Person person = new Person();
        person.travel("万里长城");
    }
}


public class Person {

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

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

        //lambda表达式
        Camera camera = str -> this.takePhoto(str);
        camera.takePhoto(name);

        //方法引用
        Camera c = this::takePhoto;
        c.takePhoto(name);
    }
}

public interface Camera {

    void takePhoto(String name);
}

1.4.2 super引用父类成员方法

语法

super::方法名

示例

public class Test {

    public static void main(String[] args) {

        JavaProgrammer javaProgrammer = new JavaProgrammer();
        javaProgrammer.communicateWithCustomer();
    }
}

public class JavaProgrammer extends SoftEngineer{

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

        //lambda表达式
//        Customer customer = () -> JavaProgrammer.super.analysisBusyness();

        Customer customer = super::analysisBusyness;
        customer.communicateBusyness();
    }
}

public class SoftEngineer {

    public void analysisBusyness() {
        System.out.println("分析业务");
    }
}

public interface Customer {

    //业务交流
    void communicateBusyness();
}

1.4.3 构造方法的引用

语法

类名::new

示例

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 = (String name, String sex) -> new Student(name, sex);

        StudentBuilder studentBuilder = Student::new;
        Student student = studentBuilder.build("张三", "男");

        System.out.println(student);
    }
}

public interface StudentBuilder {

    Student build(String name, String sex);
}

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 + '\'' +
                '}';
    }
}

2、函数式接口

官方文档说明

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

示例

//函数式接口
public interface Hello {
    //一个抽象方法
    vaid sayHello(String msg);
    
    //普通方法
    default void show() {
        "show".sout;
    }
    
    //静态方法
    static void print() {
        "print".sout;
    }
}

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

2.1 函数式编程

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

2.2 Consumer接口

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

解释说明

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

示例

public class ConsumerTest {

    public static void main(String[] args) {

//        Consumer<String> c = new Consumer<String>() {
//            @Override
//            public void accept(String s) {
//                System.out.println(s);
//            }
//        };

//        Consumer<String> c = s -> System.out.println(s);

        Consumer<String> c = System.out::println;
        c.accept("这是被消费的信息");

        Consumer<String> c2 = s -> System.out.println(s.charAt(0));
        c2.accept("this is a consumer");

        Consumer<String> c3 = c.andThen(c2);
        c3.accept("先打印再取第一个字符");

        //将数组转换成集合
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
//        numbers.forEach(new Consumer<Integer>() {
//            @Override
//            public void accept(Integer integer) {
//                System.out.println(integer);
//            }
//        });

//        numbers.forEach(integer -> System.out.println(integer));

        numbers.forEach(System.out::println);
    }
}

2.3 BigConsumer接口

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

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

2.4 Predicate接口

boolean test(T t);
default Predicate<T> and(Predicate<? super T> other);//条件之间的逻辑与衔接
default Predicate<T> negate();//条件取反
default Predicate or(Predicate<? super T> other);//条件之间的逻辑或衔接

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

2.5 Function接口

R apply(T t);
default<V> Function<T, V> andThen(Function<? super R, ? extends V> after);

Function是功能的意思,可以将一种数据类型的对象转换为另一种数据类型的对象,如何转换取决于如何实现

posted @ 2022-03-08 23:55  DarkSki  阅读(54)  评论(0编辑  收藏  举报