Java新特性-函数式接口

什么是函数式接口

有且仅有一个抽象方法的接口就是函数式接口

如何检测一个接口是不是函数式接口呢,可以通过 @FunctionalInterface 注解来进行检测,放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,会编译失败,我们自己定义函数式接口的时候,@FunctionalInterface 注解是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解

简单示例

新建一个 MyInterface 函数式接口,内容如下

/**
 * @author BNTang
 */
@FunctionalInterface
public interface MyInterface {
    void show();
}

如果这个时候在当前这个函数式接口中在定义一个新的方法这个时候就会报错,我这里不演示报错,你可以自己自行添加一个新的,然后 psvm 来进行测试一下我们的这个自定义的函数式接口吧,如下是使用匿名内部类的方式来进行使用的,先不一步到位

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {

        MyInterface myInterface = new MyInterface() {
            @Override
            public void show() {
                System.out.println("函数式接口");
            }
        };

        myInterface.show();
    }
}

接下来开始使用 Lambda 表达式来进行简化一下我们上方的写法吧,简化之后的代码如下

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        MyInterface myInterface = () -> System.out.println("函数式接口");
        myInterface.show();
    }
}

从以后的示例开始,我将不在从匿名内部类一步一步的演变到 Lambda 了,将会直接一步到位

函数式接口的应用场景

作为方法的参数

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        startThread(() -> System.out.println("线程启动"));
    }

    private static void startThread(Runnable runnable) {
        new Thread(runnable).start();
    }
}

作为方法的返回值

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        List<String> array = new ArrayList<>();

        array.add("BNTang");
        array.add("Jonathan_Lee");
        array.add("bbb");
        array.add("aa");
        array.add("cccc");

        System.out.println("排序前" + array);

        Collections.sort(array);

        System.out.println("排序后" + array);

        Collections.sort(array, getComparator());
    }

    private static Comparator<String> getComparator() {
        return (s1, s2) -> s1.length() - s2.length();
    }
}

如上是 Lambda 的方式实现的,来看看通过匿名内部类的实现吧如下

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        List<String> array = new ArrayList<>();

        array.add("BNTang");
        array.add("Jonathan_Lee");
        array.add("bbb");
        array.add("aa");
        array.add("cccc");

        System.out.println("排序前" + array);

        Collections.sort(array);

        System.out.println("排序后" + array);

        Collections.sort(array, getComparator());
    }

    private static Comparator<String> getComparator() {
        Comparator<String> comp = new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.length() - s2.length();
            }
        };
        return (String s1, String s2) -> {
            return s1.length() - s2.length();
        };
    }
}

常用的函数式接口

Supplier 接口

  • 作用:Supplier<T> 接口也被称为生产型接口, 如果我们指定了接口的泛型是什么类型,那么接口中的 get 方法就会生产什么类型的数据供我们使用
  • 常用方法
    • T get():按照某种实现逻辑(由 Lambda 表达式实现)返回一个数据

示例如下,定义一个方法,返回一个 字符串 数据

private static String getString(Supplier<String> sup) {
    return sup.get();
}

然后在定义一个方法,返回一个 整数 数据

private static Integer getInteger(Supplier<Integer> sup) {
    return sup.get();
}

psvm 进行测试,最终代码如下

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        System.out.println(getString(() -> "test"));
        System.out.println(getInteger(() -> 30));
    }

    private static Integer getInteger(Supplier<Integer> sup) {
        return sup.get();
    }

    private static String getString(Supplier<String> sup) {
        return sup.get();
    }
}

返回一个 int 数组中的最大值, 不管业务是什么, 最终的结果是指定的类型

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        int[] arr = {10, 30, 20, 100, 60};
        int maxValue = getMax(() -> {
            int max = arr[0];
            for (int i = 1; i < arr.length; i++) {
                if (arr[i] > max) {
                    max = arr[i];
                }
            }
            return max;
        });
        System.out.println(maxValue);
    }

    private static int getMax(Supplier<Integer> sup) {
        return sup.get();
    }
}

Consumer 接口

  • 作用:Consumer<T> 接口也被称为消费型接口, 它消费数据的数据类型由泛型指定
  • 常用方法
    • void accept(T t):对给定的参数执行此操作
    • andThen(Consumer after):返回一个组合的 Consumer,依次执行此操作,然后执行 after 操作

accept 方法的示例如下, 定义一个方法,消费一个字符串数据

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        operatorString("abc", s -> System.out.println(new StringBuilder(s).reverse().toString()));
    }

    private static void operatorString(String name, Consumer<String> con) {
        con.accept(name);
    }
}

andThen 方法示例如下,传入两个操作使用 andThen 完成

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        operatorString("abc", System.out::println, (str) -> System.out.println(new StringBuilder(str).reverse().toString()));
    }

    private static void operatorString(String name, Consumer<String> con1, Consumer<String> con2) {
        con1.andThen(con2).accept(name);
    }
}

练习:需求,把一个字符串数组按以下格式输出

String[] strArray = {"zs, 30", "ls, 35", "wc, 33"};

姓名:zs, 年龄:30
姓名:ls, 年龄:35
姓名:wc, 年龄:33

代码实现如下

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        String[] strArray = {"zs,30", "ls,35", "wc,33"};
        printInfo(strArray,
                str -> System.out.print("姓名:" + str.split(",")[0]),
                str -> System.out.println(",年龄:" + Integer.parseInt(str.split(",")[1])));
    }

    private static void printInfo(String[] strArray, Consumer<String> con1, Consumer<String> con2) {
        for (String str : strArray) {
            con1.andThen(con2).accept(str);
        }
    }
}

Predicate 接口

  • 作用:Predicate<T> 接口通常用于判断参数是否满足指定的条件
  • 常用方法
    • boolean test(T t):对给定的参数进行判断(判断逻辑由 Lambda 表达式实现) 返回一个布尔值
    • default Predicate<T> negate():返回一个逻辑的否定,对应逻辑非
    • default Predicate<T> and(Predicate other):返回一个组合判断,对应逻辑运算符中的短路与
    • default Predicate<T> or(Predicate other):返回一个组合判断,对应逻辑运算符中的短路或

test 方法示例如下, 判断给定的字符串是否满足要求

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        System.out.println(checkString("Hello", s -> s.length() > 8));
        System.out.println(checkString("HelloWorld", s -> s.length() > 8));
    }

    private static boolean checkString(String s, Predicate<String> pre) {
        return pre.test(s);
    }
}

negate 方法示例如下,返回一个逻辑的否定,对应逻辑运算符中的逻辑非,判断给定的字符串是否满足要求,对结果进行取返操作

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        System.out.println(checkString("Hello", s -> s.length() > 8));
        System.out.println(checkString("HelloWorld", s -> s.length() > 8));
    }

    private static boolean checkString(String s, Predicate<String> pre) {
        return pre.negate().test(s);
    }
}

and,返回一个组合判断,对应逻辑运算符中的短路与,判断给定的字符串是否满足要求

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        System.out.println(checkString("Hello", s -> s.length() > 8, s -> s.length() < 15));
    }

    private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
        return pre1.and(pre2).test(s);
    }
}

or,返回一个组合判断,对应逻辑运算符中的短路或,判断给定的字符串是否满足要求

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        System.out.println(checkString("Hello", s -> s.length() > 8, s -> s.length() < 15));
    }

    private static boolean checkString(String s, Predicate<String> pre1, Predicate<String> pre2) {
        return pre1.or(pre2).test(s);
    }
}

练习, 假如一个需求如:给定一个数组 String[] strArray = {"ab,30", "abc ,34", "abc,35", "ab,31", "abc,33"};,字母长度大于 2;数字大于 33, 代码实现如下

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        String[] strArray = {"ab,30", "abc ,34", "abc,35", "ab,31", "abc,33"};

        List<String> array = myFilter(strArray, s -> s.split(",")[0].length() > 2,
                s -> Integer.parseInt(s.split(",")[1]) > 33);
        for (String str : array) {
            System.out.println(str);
        }
    }

    public static List<String> myFilter(String[] strArray, Predicate<String> pre1, Predicate<String> pre2) {
        List<String> array = new ArrayList<>();

        for (String str : strArray) {
            if (pre1.and(pre2).test(str)) {
                array.add(str);
            }
        }
        return array;
    }
}

Function 接口

  • 作用:Function<T, R> 接口通常用于对参数进行处理,转换(处理逻辑由 Lambda 表达式实现) 然后返回一个新的值
  • 常用方法
    • R apply(T t):将此函数应用于给定的参数
    • default <V> Function andThen(Function after):返回一个组合函数,首先将该函数应用于输入,然后将 after 函数应用于结果

apply 方法示例如下,定义一个方法,把一个字符串转换 int 类型,在控制台中输出

private static void convert(String s, Function<String, Integer> fun) {
    System.out.println(fun.apply(s));
}

定义一个方法,把一个 int 类型的数据加上一个整数之后,转为字符串在控制台中输出

private static void convert(int i, Function<Integer, String> fun) {
    System.out.println(fun.apply(i));
}

最终代码如下所示

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        convert("88", Integer::parseInt);
        convert(88, i -> String.valueOf(i + 888));
    }

    private static void convert(String s, Function<String, Integer> fun) {
        System.out.println(fun.apply(s));
    }

    private static void convert(int i, Function<Integer, String> fun) {
        System.out.println(fun.apply(i));
    }
}

andThen 方法示例如下所示,定义一个方法,把一个字符串转换为 int 类型,然后在把 int 类型的数据加上一个整数之后,转为字符串在控制台中输出

/**
 * @author BNTang
 */
public class Main {
    public static void main(String[] args) {
        convert("100", Integer::parseInt, i -> String.valueOf(i + 788));
    }

    private static void convert(String s, Function<String, Integer> fun1, Function<Integer, String> fun2) {
        System.out.println(fun1.andThen(fun2).apply(s));
    }
}
posted @ 2021-02-13 19:38  BNTang  阅读(99)  评论(0编辑  收藏  举报