函数式接口

函数式接口

1、概念、格式

函数式接口在Java中是指只有一个抽象方法的接口,但是接口中还可以有其他方法,比如public、default等,只有确保接口只有一个抽象方法,Lambda才能进行顺利的推导。

格式

@FunctionalInterface// 使用注解来检测该接口是否函数式接口,也就是是否只有一个抽象方法
修饰符 interface 接口名称 {
    public abstract 返回值类型 方法名称(参数列表);
}

函数式接口作为方法的参数

package cn.zhuobo.day16.aboutFuntionalInterface;

//函数式接口作为一个方法的参数,有三种用法去调用这个方法

public class MyFunctionalInterfaceMain {
    public static void main(String[] args) {
        
        // 1、创建一个实现类对象传递给show方法,该实现类里重写了抽象方法
        show(new MyFunctionnalInterfaceImpl());
        
        // 2、创建一个匿名内部类,传递给show方法
        show(new MyfunctionalInterface() {
            @Override
            public void method() {
                System.out.println("这是使用匿名内部类来实现的");
            }
        });

        // 3、使用Lambda表达式简化匿名内部类的书写
        show(() -> {
            System.out.println("这是使用Lambda表达式实现的");
        });
    }

    public static void show(MyfunctionalInterface myInter) {
        myInter.method();
    }
}

注意:Lambda表达式在应用上可以看作是一种语法糖,简化了匿名内部类的书写,但是,事实上他他们的原理是不一样的(for each 表达式是简化迭代器书写的一种语法糖,他们的实现原理都是一样,都是使用迭代器),Lambda表达式不会生成一个class文件,但是使用匿名内部类会生成一个xxx$1.class文件。

2、函数式编程

2.1 延迟执行,函数式接口作为方法的参数

有时候,我们的代码的执行结果不会被马上使用,或者没有产生有用的结果,就会发生性能浪费的事情。就像下面的例子一样,一个方法的传递给方法的参数是三个字符串的拼接,也就是拼接字符串出现在判断level == 1之前,也就是说哪怕level != 1,也会初心字符串拼接,但是此时这些拼接的字符串就显得没有意义了,因为根本没有什么用。也就是发生了性能浪费。

package cn.zhuobo.day16.logger;

public class LoggerDemo1 {
    public static void main(String[] args) {
        String mess1 = "你是";
        String mess2 = "猪猪";
        String mess3 = "吗?";

        showMessage(2, mess1 + mess2 + mess3);
    }
    public static void showMessage(int level, String message) {
        if(level == 1) {
            System.out.println(message);
        }
    }
}

Lambda表达式的延迟执行:把Lambda表达式作为参数传递到方法中,只有满足条件,这样才会执行Lambda表达式的代码块,这样就可以避免性能的浪费。

一个接口

package cn.zhuobo.day16.logger;

@FunctionalInterface
public interface LoggerInterface {
    public abstract String showMessage();// 返回String
}

性能优化

package cn.zhuobo.day16.logger;

public class LoggerDemo02 {
    public static void main(String[] args) {
        String mess1 = "你是";
        String mess2 = "猪猪";
        String mess3 = "吗?";
        method(2, () -> {
            return mess1 + mess2 + mess3;
        });
    }

    public static void method(int level, LoggerInterface logger) {
        if(level == 1)
            System.out.println(logger.showMessage());
    }
}

仔细梳理一下就会发现:接口的抽象方法返回的是字符串,根据Lambda表达式,我们可以知道他返回的是一个拼接后的字符串。同时,在method方法中,只有level==1才会调用接口的showMessage方法,这样才会进行字符串的拼接,反之就不会进行字符串拼接。没有性能浪费现象。

2.2 函数式接口作为方法的返回值

如果一个方法返回的是一个函数式接口,那么就可以返回一个Lambda表达式(当然也可以返回实现类对象,匿名内部类对象)

下面的例子是通过一个方法获得一个java.util.Comparator接口类型的对象作为Arrays.sort方法的参数,对数组进行排序,这个方法就返回一个函数式接口。

package cn.zhuobo.day16.functionalInterfaceReturn;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;

public class Demo01 {

    // 这个方法的返回值是一个函数式接口,那么可以返回一个匿名内部类
    public static Comparator<String> getComparator1() {
        return new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();// 按照字符串的长度升序排序
            }
        };
    }
    // 这个方法的返回值是一个函数式接口,返回一个Lambda表达式
    public static Comparator<String> getComparator2() {
        return (o1, o2) -> o1.length() - o2.length();
    }

    public static void main(String[] args) {
        String[] arr = {"aa", "bbbb", "ccccccccccc", "dd", "adf"};
        System.out.println(Arrays.toString(arr));

        Arrays.sort(arr, getComparator2());
        System.out.println(Arrays.toString(arr));
    }
}

2.3 常用的函数式接口

java.util.function中包含了大量的函数式接口

  • java.util.function.Supplier

该接口仅包含一个无参数的方法T get(),用来获取一个泛型参数指定类型的对象数据(生产数据),指定接口的泛型T是什么,那么get方法就会生产(返回)什么类型的数据

package cn.zhuobo.day16.aboutFuntionalInterface;

import java.util.function.Supplier;

public class InterfaceSupplier {
    public static String getString(Supplier<String> sup) {
        //System.out.println("shshs");
        return sup.get();
    }

    public static void main(String[] args) {// getString方法是函数式接口,因此可以传递Lambda表达式
        String str = getString(() -> {return "bilibili";});
        System.out.println(str);
    }
}
  • java.util.function.Consumer

Consumer接口是和Supplier接口相反,不是生产一个数据,而是消费一个数据,该接口中包含抽象方法void accept(T, t),意思是消费一个数据,泛型T指定的是什么类型就消费什么类型的数据,至于具体怎么消费(也就是怎么使用数据),那就要根据自己的需要的(输出,运算.......)

package cn.zhuobo.day16.aboutFuntionalInterface;

import java.util.function.Consumer;

public class InterfaceConsumer {
    // 一个函数式接口作为参数
    public static void method(String message, Consumer<String> con) {
        con.accept(message);
    }

    public static void method2(String message, Consumer<String> con1, Consumer<String> con2) {
        con1.andThen(con2).accept(message);// 先将两个接口连接到一起再消费数据,但是还是调用者先消费,也就con1先消费
    }

    public static void main(String[] args) {
        String message = "Hello";
        // 参数里可以写Lambda表达式,字节决定如何重写accept方法,这里是是字符串翻转
        method(message, (s) -> {
            String reMessage = new StringBuilder(s).reverse().toString();
            System.out.println(reMessage);
        });


        method2(message, (s) -> {
            System.out.println(s.toUpperCase());
        }, (s) -> {
            System.out.println(s.toLowerCase());
        });
    }
}

  • java.util.function.Predicate

该接口用来对某种类型的数据进行判断,结果返回一个boolean值,可以使用接口的一个抽象方法boolean test(T t)判断,结果符合条件返回true,否则返回false

Predicate接口中三个默认方法andornegate

package cn.zhuobo.day16.aboutFuntionalInterface;

import java.util.function.Predicate;

public class InterfacePredicate {
    // 一个条件
    public static boolean stringPredicate1(String message, Predicate<String> pre){
        return pre.test(message);// 调用test对message进行判断
    }
    
    // and方法
    public static boolean stringPredicate2(String message, Predicate<String> pre1, Predicate<String> pre2) {
        //return pre1.test(message) && pre2.test(message);
        // 使用and方法有一样的效果
        return pre1.and(pre2).test(message);
    }

    // or方法
    public static boolean stringPredicate3(String message, Predicate<String> pre1, Predicate<String> pre2) {
        //return pre1.test(message) && pre2.test(message);
        // 使用and方法有一样的效果
        return pre1.or(pre2).test(message);
    }
    
    //negate方法
    public static boolean stringPredicate4(String message, Predicate<String> pre){
        return pre.negate().test(message);// 调用test对message进行判断
    }
    
    public static void main(String[] args) {
        String message = "hellollo";

        boolean b1 = stringPredicate1(message, (str) -> {// 这里写判断的逻辑
            return str.length() > 6;
        });
        System.out.println(b1);

        boolean b2 = stringPredicate2(message, (str)->{
            return str.length() > 6;
        }, (str)->{
            return str.contains("j");
        });
        System.out.println(b2);
    }
}
  • java.util.function.Function<T, R>

该接口用来根据一个类型的数据的到另一个类型的数据,前者为前置条件,后者为后置条件。该接口最主要的抽象方法是 R apply(T, t),根据T类型的参数获取R类型的参数

默认方法:andthen,与consumer接口类似的情况,当使用两个Function接口作为方法的参数时,就可以类似的使用(也就是可以使用andThen方法,将两次转换连接到一起,还是一样的先进行andThen的调用者。)

原文地址:https://www.cnblogs.com/zhuobo/p/10665452.html