函数式接口

  • 函数式接口:有且仅有一个抽象方法的接口
  • 函数式接口适用于Lambda表达式
  • 只有确保接口中有且仅有一个抽象方法,Lambda才能顺利推导

定义一个函数式接口

@FunctionalInterface    //此注解表明是函数式接口
public interface MyInt {
    void show();
}

测试类

public class MyInterDemo {
    public static void main(String[] args) {
        /*函数式接口可以 用作参数传递,用作局部变量*/
        //1.用作局部变量
        MyInt mi = ()->{
            System.out.println("函数式接口用作局部变量");
        };
        mi.show();
    }
}

运行结果

函数式接口用作局部变量

注意

  • 满足函数式接口的情况下,@FunctionalInterface 可写可不写,建议写上
  • 标注@FunctionalInterface的情况下,如果是函数式接口,编译通过;如果不是函数式接口,编译不通过

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

需求:定义一个类RunnableDemo,在里面提供两个方法
一个方法是startThread(Runnable r),方法参数Runnable是一个函数式接口
在住方法中调用startThread方法

public class RunnableDemo {
    public static void main(String[] args) {
        //1.采用匿名内部类方式调用startThread方法
        startThread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + "线程启动了");
            }
        });
        //2.采用Lambda表达式方式调用startThread方法
        startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
    }
    /**
     * 函数式接口作为方法的参数
     *
     * @param r 函数式接口
     */
    private static void startThread(Runnable r) {
        new Thread(r).start();
    }
}

运行结果

Thread-1线程启动了
Thread-0线程启动了

注意

  • 如果方法的参数是一个函数式接口,可以使用Lambda表达式作为参数传递

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

需求:定义一个类ComparatorDemo,在里面提供两个方法
一个方法是Comparator< String> getComparator, 方法返回值Comparator是一个函数式接口
在住方法中调用getComparator方法

public class ComparatorDemo {
    public static void main(String[] args) {
        //对字符串长度进行排序
        //1.定义集合,存储字符串元素
        List<String> list = new ArrayList<>();
        list.add("hello");
        list.add("word");
        list.add("javase");
        System.out.println("排序前:" + list);
        //2.排序
        //自然排序
        Collections.sort(list);
        System.out.println("自然排序后:" + list);
        //比较器排序
        Collections.sort(list,getCompara());
        System.out.println("比较器排序后:" + list);
    }
    /**
     * 比较器排序
     *
     * @return
     */
    private static Comparator<String> getCompara() {
        //Lambda表达式方式,字符串长度由小到大
        return (s1, s2) -> s1.length() - s2.length();
    }
}

运行结果

排序前:[hello, word, javase]
自然排序后:[hello, javase, word]
比较器排序后:[word, hello, javase]

注意

  • 如果方法的返回值是一个函数式接口,可以使用Lambda表达式作为结果返回

常用函数式接口

Java8在Java.util.function包下提供了大量函数式接口

  • Supplier
  • Consumer
  • Predicate
  • Function

Supplier接口

Supplier < T >:包含一个无参的方法

  • T get():获得结果
  • 该方法不需要参数,他会按照某种实现逻辑(Lambda表达式)返回一个数据
  • Supplier < T >被称为生产型接口,如果我们指定了接口的泛型,那么get()就会生产什么样的数据

案例1:

public class SupplierDemo {
    public static void main(String[] args) {
        //接受返回的String类型的数据
        //String s = getString(() -> {
        //    return "世界杯";
        //});
        String s = getString(() -> "世界杯");
        Integer i = getInt(() -> 13);
        //输出数据
        System.out.println(s);
        System.out.println(i);
    }
    /**
     * 返回接口泛型类型的数据
     *
     * @param str
     * @return
     */
    private static String getString(Supplier<String> str) {
        return str.get();
    }
    private static Integer getInt(Supplier<Integer> in) {
        return in.get();
    }
}

运行结果:

世界杯
13

案例2:

需求:定义一个类SupplierTest,里面有一个int getMax(Supplier< Integer> sup)用于返回int数组中的最大值

public class SupplierTest {
    public static void main(String[] args) {
        //定义int数组
        int[] arr = {5, 12, 65, 78, 22, 11};
        //调用方法,并接收返回的数据
        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);
    }
    /**
     * 返回int数组中的最大值
     *
     * @param sup
     * @return
     */
    private static int getMax(Supplier<Integer> sup) {
        return sup.get();
    }
}

运行结果:

78

Consumer接口

Consumer< T>:包含两个方法

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

案例:

public class ConsumerDemo {
    public static void main(String[] args) {
        //消费一个字符串数据, 输出字符串
        opratorString("世界杯", s -> {
            System.out.println(s);
        });
        opratorString("世界杯", s -> System.out.println(s));
        opratorString("世界杯", System.out::println);
        //对字符串进行反转,并输出
        opratorString("世界杯", s -> System.out.println(new StringBuilder(s).reverse().toString()));
        System.out.println("------------------------");
        //先输出字符串,然后输出反转后的字符串
        opratorString("世界杯", System.out::println,s -> System.out.println(new StringBuilder(s).reverse().toString()));
    }
    /**
     * 定义一个方法,消费一个字符串数据
     *
     * @param str
     * @param con
     */
    private static void opratorString(String str, Consumer<String> con) {
        con.accept(str);
    }
    /**
     * 方法重载
     * 定义一个方法,用不同的方式,消费同一个字符串数据两次
     *
     * @param str
     * @param con1
     * @param con2
     */
    private static void opratorString(String str, Consumer<String> con1, Consumer<String> con2) {
        //con1.accept(str);
        //con2.accept(str);
        //上述代码,可以替换成
        con1.andThen(con2).accept(str);
    }
}

运行结果:

世界杯
世界杯
世界杯
杯界世
------------------------
世界杯
杯界世

练习:

  • String[] strArray = {"李信,30","苏烈,32","白起,40"};
  • 字符串数组有多条信息,请按照格式:“姓名:XX,年龄:XX”的格式将信息打印出来
  • 要求:
    -- 把打印姓名的动作作为第一个Consumer接口的Lambda实例
    -- 把打印年龄的动作作为第二个Consumer接口的Lambda实例
    -- 将两个Consumer接口按照顺序组合到一起使用
public class ConsumerTest {
    public static void main(String[] args) {
        //定义字符串数组
        String[] strArray = {"李信,30", "苏烈,32", "白起,23"};
        //调用方法
        /*printInfo(strArray, str -> {
                    String name = str.split(",")[0];
                    System.out.print("姓名:" + name);
                },
                str -> {
                    int age = Integer.parseInt(str.split(",")[1]);
                    System.out.println(",年龄:" + age);
                }
        );*/
		//优化
        printInfo(strArray, str -> System.out.print("姓名:" + str.split(",")[0]),
                str -> System.out.println(",年龄:" + Integer.parseInt(str.split(",")[1]))
        );
    }
    /**
     * 遍历字符串数组
     *
     * @param strArrary
     * @param con1
     * @param con2
     */
    private static void printInfo(String[] strArrary, Consumer<String> con1, Consumer<String> con2) {
        for (String s : strArrary) {
            con1.andThen(con2).accept(s);
        }
    }
}

运行结果:

姓名:李信,年龄:30
姓名:苏烈,年龄:32
姓名:白起,年龄:23

Predicate接口

函数式接口
案例:

public class PredicateDemo {
    public static void main(String[] args) {
        //判断字符串长度是否大于8
        System.out.println(checkString("Hello", s -> s.length() > 8));
        System.out.println(checkString("HelloJava", s -> s.length() > 8));
        //字符串长度是否大于8 跟 字符串长度是否小于15 做与运算
        System.out.println(checkString("hello", s -> s.length() > 8, s -> s.length() < 15));
        System.out.println(checkString("helloJava", s -> s.length() > 8, s -> s.length() < 15));
        //字符串长度是否大于8 跟 字符串长度是否小于15 做或运算
        System.out.println(checkString1("hello", s -> s.length() > 8, s -> s.length() < 15));
        System.out.println(checkString1("helloJava", s -> s.length() > 8, s -> s.length() < 15));
    }
    /**
     * 判断给定的字符串是否满足要求
     *
     * @param str
     * @param pre
     * @return
     */
    private static boolean checkString(String str, Predicate<String> pre) {
        return pre.test(str);
        //negate()逻辑非判断,即否定判断
        //return pre.negate().test(str);
    }
    /**
     * 同一个字符串给出两个不同的判断条件,返回两个条件的结果做逻辑与运算的结果
     * @param str
     * @param pre1
     * @param pre2
     * @return
     */
    private static boolean checkString(String str, Predicate<String> pre1,Predicate<String> pre2){
        //return pre1.test(str) && pre2.test(str);
        return pre1.and(pre2).test(str);
    }
    /**
     * 同一个字符串给出两个不同的判断条件,返回两个条件的结果做逻辑或运算的结果
     * @param str
     * @param pre1
     * @param pre2
     * @return
     */
    private static boolean checkString1(String str, Predicate<String> pre1,Predicate<String> pre2){
        return pre1.or(pre2).test(str);
    }
}

运行结果:

false
true
false
true
true
true

注意:加上negate()就是否定判断,结果相反

练习:

  • String[] strArray = {"刘亦菲,30","刘萱,34","李冰冰,35","大乔,31","孙尚香,33"};
  • 字符串数组有多条信息,请通过Predicate接口的拼装,将符合要求的字符串筛选到集合ArraryList集合中,并遍历
  • 同时满足如下要求:姓名长度大于2,年龄大于33
  • 分析:有两个判断条件,需要使用两个Predicate接口,对条件进行判断;必须同时满足两个条件,可以使用and方法连接两个判断条件
public class PredicateTest {
    public static void main(String[] args) {
        //定义字符串数组
        String[] strArray = {"刘亦菲,30", "刘萱,34", "李冰冰,35", "大乔,31", "孙尚香,33"};
        //获取符合条件的元素,加入集合中
        ArrayList<String> arr = check(strArray, s -> s.split(",")[0].length() > 2,
                s -> Integer.parseInt(s.split(",")[1]) > 33);
        //遍历集合
        for (String s : arr) {
            System.out.println(s);
        }
    }
    private static ArrayList<String> check(String[] strArray, Predicate<String> p1, Predicate<String> p2) {
        //定义集合
        ArrayList<String> list = new ArrayList<>();
        //遍历字符串数组
        for (String s : strArray) {
            //如果元素符合条件,就把元素添加到集合里
            if (p1.and(p2).test(s)) {
                list.add(s);
            }
        }
        return list;
    }
}

运行结果:

李冰冰,35

Function接口

函数式接口
案例:

public class FuctionDemo {
    public static void main(String[] args) {
        //字符串转int
        convert("100", s -> Integer.parseInt(s));
        //方法引用改进
        convert("100", Integer::parseInt);
        //int类型的数据加上一个数字,转为字符串输出
        convert(100, s -> String.valueOf(s + 566));
        //字符串转int类型的数据,加上一个数字之后,转为字符串输出
        convert("100", Integer::parseInt, s -> String.valueOf(s + 788));
    }
    /**
     * 字符串转int
     *
     * @param str
     * @param fun
     */
    private static void convert(String str, Function<String, Integer> fun) {
        System.out.println(fun.apply(str));
    }
    /**
     * int类型的数据做操作之后,转为字符串输出
     *
     * @param i
     * @param fun
     */
    private static void convert(int i, Function<Integer, String> fun) {
        System.out.println(fun.apply(i));
    }
    /**
     * 字符串转int类型的数据,然后做操作之后,转为字符串输出
     *
     * @param str
     * @param f1
     * @param f2
     */
    private static void convert(String str, Function<String, Integer> f1, Function<Integer, String> f2) {
        System.out.println(f1.andThen(f2).apply(str));
    }
}

运行结果:

100
100
666
888

练习:按照指定要求操作数据

  • String s = "刘亦菲,30";
  • 请通过Function接口实现函数拼接
  • 请按照指定的要求进行操作
  • 1.截取字符串,得到年龄
  • 2.将截取到的年龄字符串,转为int类型的数据
  • 3.将转化后的int类型的数据,加70,得到新的int类型的数据,并输出
public class FunctionTest {
    public static void main(String[] args) {
        String s = "刘亦菲,30";
        operator(s, a -> a.split(",")[1], a -> Integer.parseInt(a), a -> a + 70);
        operator(s, a -> a.split(",")[1], Integer::parseInt, i -> i + 70);
    }
    private static void operator(String str, Function<String, String> f1, Function<String, Integer> f2,
                                 Function<Integer, Integer> f3) {
        System.out.println(f1.andThen(f2).andThen(f3).apply(str));
    }
}

运行结果:

100
100

发表回复