飞道的博客

23 Java的函数式接口介绍

340人阅读  评论(0)

函数式接口

一、函数式接口

1. 概念

  • **函数式接口:**有且仅有一个抽象方法的接口,称之为函数式接口。

  • 接口中可以包含其他方法:默认、静态、私有。

  • @FunctionalInterface注解

    • 作用:可以检测接口是否是一个函数式接口

      • 是:编译成功
      • 否:编译失败(接口中没有抽象方法或抽象方法的个数 )
    • 格式:

      @FunctionalInterface
      public interface MyFunctionalInterface {
        //定义一个抽象方法
        public abstract void method();
      }
      

2. 函数式接口的使用

  • 一般可作为方法的参数和返回值类型。

    //接口
    public interface MyFunctionalInterface {
        public abstract void method();
    }
    
    //接口的实现类
    public class MyFunctionalInterfaceImpl implements MyFunctionalInterface {
        @Override
        public void method() {
            System.out.println("使用接口的实现类重写接口的方法");
        }
    }
    
    //测试类
    public class Test {
        //函数式接口作为方法的参数
        public static void show(MyFunctionalInterface myFunctionalInterface) {
            myFunctionalInterface.method();
        }
    
        public static void main(String[] args) {
            //使用接口的实现类作为参数
            show(new MyFunctionalInterfaceImpl());
    
            //使用匿名内部类作为参数
            show(new MyFunctionalInterface() {
                @Override
                public void method() {
                    System.out.println("使用匿名内部类重写接口的方法");
                }
            });
    
            //使用lambda表达式作为参数
            show(()->{
                System.out.println("使用lambda表达式重写接口的方法");
            });
    
            //使用简化的lambda表达式作为参数
            show(()-> System.out.println("使用简化的lambda表达式重写接口的方法"));
        }
    }
    

二、函数式编程

1. Lambda的延迟执行

  • 有些场景的代码执行后,结果不一定会被使用,从而造成性能浪费。而Lambda表达式是延迟执行的,这正好可以作为解决方案,提升性能。

2. 性能浪费的日志案例

/*
  以下代码存在一些性能浪费的问题:
  调用showLog方法,传递的第二个参数是一个拼接好的字符串,
  先把字符串拼接好,然后调用showLog方法,
  如果showLog方法中第一个参数传递的日志等级不是1级,
  那么就不会输出拼接后的字符串,
  这样字符串就白白拼接了,造成了性能浪费。
*/

public class Logger {
    public static void showLog(int level, String message) {
        if (level == 1) {
            System.out.println(message);
        }
    }

    public static void main(String[] args) {
        String msg1 = "Hello";
        String msg2 = "World";
        String msg3 = "Java";

        showLog(1,msg1+msg2+msg3);
        showLog(2,msg1+msg2+msg3);
    }
}

3. 使用Lambda表达式优化日志案例

  • Lambda的特点:延迟执行。

  • Lambda的使用前提:必须存在函数式接口。

    /*
    	使用Lambda表达式作为参数传递,仅仅是把参数传递到showLog方法中,
    	只有满足条件(日志的等级为1),
        才会调用接口MessageBuilder中的方法builderMessage,
        才会进行字符串的拼接;
      如果条件不满足(日志的等级不为1),
      	那么接口MessageBuilder中的方法builderMessage不会执行,
      	所以拼接字符串的代码也不会执行;
      所以不会存在性能的浪费。
    */
    
    public class Lambda {
        public static void showLog(int level, MessageBuilder message) {
            if (level == 1) {
                System.out.println(message.builderMessage());
            }
        }
    
        public static void main(String[] args) {
            String msg1 = "Hello";
            String msg2 = "World";
            String msg3 = "Java";
    
            showLog(1,()->{
                return msg1+msg2+msg3;
            });
    
            showLog(2,()->{
                return msg1+msg2+msg3;
            });
        }
    }
    

三、常用函数式接口

1. Supplier接口

  • java.util.function.Supplier<T>:接口仅包含一个无参的方法T get(),用来获取一个泛型参数指定类型的对象数据。

  • Supplier<T>接口被称之为生产型接口,指定接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据。

    import java.util.function.Supplier;
    
    public class Test {
        //生产一个String类型的数据
        public static String getString(Supplier<String> supplier) {
            return supplier.get();
        }
    
        public static void main(String[] args) {
            //使用Lambda表达式
            String str1 = getString(()->{
                return "Hello,World!";
            });
            System.out.println(str1);
            
            //使用简化的Lambda表达式
            String str2 = getString(()->"Hello,World!");
            System.out.println(str2);
    
        }
    }
    
  • 练习:求数组元素最大值

    /*
    	使用Supplier接口作为方法参数类型,通过Lambda表达式求出int数组中的最大值
    */
    import java.util.function.Supplier;
    
    public class Test {
        public static int getMax(Supplier<Integer> supplier) {
            return supplier.get();
        }
    
        public static void main(String[] args) {
            int[] arr = {1,-5,7,2,10,3};
    
            int maxValue = getMax(()->{
                int max = arr[0];
                for (int i = 1; i < arr.length; i++) {
                    if (max < arr[i]) {
                        max = arr[i];
                    }
                }
                return max;
            });
            System.out.println(maxValue);
        }
    }
    

2. Consumer接口

  • java.util.function.Consumer<T>接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,其数据类型由泛型决定。

  • Consumer接口中包含抽象方法void accept(T t),意为消费一个指定泛型的数据。至于如何消费(使用),需要自定义。

    import java.util.function.Consumer;
    
    public class Test {
        public static void method(String name, Consumer<String> consumer) {
            consumer.accept(name);
        }
    
        public static void main(String[] args) {
            //输出字符串
            method("zhuguli",(String name)->{
                System.out.println(name); // zhuguli
            });
    
            //翻转字符串
            method("zhuguli",(String name)->{
                String str = new StringBuffer(name).reverse().toString();
                System.out.println(str); // iluguhz
            });
        }
    }
    
  • 默认方法:andThen

    • 作用:需要两个或多个Consumer接口,可以把两个或多个Consumer接口组合到一起,再对数据进行消费。

    • 例如:

      Consumer<String> consumer1;
      Consumer<String> consumer2;
      String str;
      consumer1.accept(str);
      consumer2.accept(str);
      
      //使用andThen
      consumer1.andThen(consumer2).accept(str);
      
    • consumer1.andThen(consumer2).accept(str);谁写在前边谁先消费。

    import java.util.function.Consumer;
    
    public class Test {
        public static void method(String str, Consumer<String> consumer1, Consumer<String> consumer2){
            consumer1.andThen(consumer2).accept(str);
        }
    
        public static void main(String[] args) {
            String str = "Hello";
            method(str,(String s)->{
                System.out.println(s.toUpperCase()); // HELLO
            },(String s)->{
                System.out.println(s.toLowerCase()); // hello
            });
        }
    }
    
  • 练习:格式化打印信息

    /*
    	字符串数组中存有多条信息,请按照格式”姓名:xx,年龄:xx。“的格式将信息打印出来。
    	要求:
    		将打印姓名的动作作为第一个Consumer接口的Lambda实例;
    		将打印年龄的动作作为第二个Consumer接口的Lambda实例;
    		将两个Consumer接口按照顺序”拼接“到一起。
    */
    import java.util.function.Consumer;
    
    public class Test {
        public static void printInfo(String[] arr, Consumer<String> consumer1, Consumer<String> consumer2){
            for (String str : arr) {
                consumer1.andThen(consumer2).accept(str);
            }
        }
    
        public static void main(String[] args) {
            String[] arr = {"朱古力,18", "猪猪侠,20", "猪猪猪,22"};
            printInfo(arr, (String str)->{
                String name = str.split(",")[0];
                System.out.print("姓名:" + name + ",");
            }, (String str)->{
                String age = str.split(",")[1];
                System.out.println("年龄:" + age + "。");
            });
        }
    }
    

3. Predicate接口

  • java.util.function.Predicate<T>接口

    • 作用:对某种数据类型的数据进行判断,结果返回一个boolean值。
  • 抽象方法:boolean test(T t)

    • 作用:用来对指定的数据类型数据进行判断的方法。
    • 结果:
      • 符合条件,返回true
      • 不符合条件,返回false
    import java.util.function.Predicate;
    
    public class Test01 {
        public static boolean checkString(String str, Predicate<String> predicate) {
            return predicate.test(str);
        }
    
        public static void main(String[] args) {
            //检测字符串的长度是否大于3
            String str = "hello";
            boolean bool = checkString(str,(String string)->{
                return string.length() > 3;
            });
            System.out.println(bool);  // true
        }
    }
    
  • 默认方法:and

    • 作用:表示并且关系,用于连接两个判断条件
    /*
    	定义一个方法,方法的参数:
    		一个字符串;
    		一个用于判断字符串的长度是否大于5;
    		一个用于判断字符串中是否包含a
    		必须同时满足两个条件
    */
    import java.util.function.Predicate;
    
    public class Test02 {
        public static boolean checkString(String str, Predicate<String> pre1, Predicate<String> pre2){
            return pre1.and(pre2).test(str); // 等价于 return pre1.test(str) && pre2.test(str);
        }
    
        public static void main(String[] args) {
            String str = "abcdef";
            boolean bool = checkString(str, (String string)->{
                return string.length() > 5;
            }, (String string)->{
                return str.contains("a");
            });
            System.out.println(bool);   // true
        }
    }
    
  • 默认方法:or

    • 作用:表示关系,用于连接两个判断条件
    /*
    	定义一个方法,方法的参数:
    		一个字符串;
    		一个用于判断字符串的长度是否大于5;
    		一个用于判断字符串中是否包含a
    		必须同时满足两个条件
    */
    import java.util.function.Predicate;
    
    public class Test03 {
        public static boolean checkString(String str, Predicate<String> pre1, Predicate<String> pre2){
            return pre1.or(pre2).test(str); // 等价于 return pre1.test(str) || pre2.test(str);
        }
    
        public static void main(String[] args) {
            String str = "bcdefg";
            boolean bool = checkString(str, (String string)->{
                return string.length() > 5;
            }, (String string)->{
                return str.contains("a");
            });
            System.out.println(bool);   // true
        }
    }
    
  • 默认方法:negate

    • 作用:表示取反
    /*
    	定义一个方法,方法的参数:
    		一个字符串;
    		一个用于判断字符串的长度是否大于5;
    */
    import java.util.function.Predicate;
    
    public class Test04 {
        public static boolean checkString(String str, Predicate<String> pre){
            return pre.negate().test(str); // 等价于 return !pre1.test(str) ;
        }
    
        public static void main(String[] args) {
            String str = "bcdefg";
            boolean bool = checkString(str, (String string)->{
                return string.length() > 5;
            });
            System.out.println(bool);   // false
        }
    }
    
  • 练习:集合信息筛选

    /*
    	数组当中有多条“姓名+性别”的信息如下:
    	String[] array = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"};
    	请通过Predicate接口的拼装将符合要求的字符串筛选到集合ArrayList中,
    	需要同时满足两个条件:
    		必须为女生
    		姓名为四个字
    */
    import java.util.ArrayList;
    import java.util.function.Predicate;
    
    public class Test {
        public static ArrayList<String> checkArray(String[] arr, Predicate<String> pre1, Predicate<String> pre2) {
            ArrayList<String> list = new ArrayList<>();
            for (String s : arr) {
                if (pre1.and(pre2).test(s)) {
                    list.add(s);
                }
            }
            return list;
        }
    
        public static void main(String[] args) {
            String[] array = {"迪丽热巴,女","古力娜扎,女","马尔扎哈,男","赵丽颖,女"};
    
            ArrayList<String> list = checkArray(array,(String string)->{
                return string.split(",")[1].equals("女");
            },(String string)->{
                return string.split(",")[0].length() == 4;
            });
    
            for (String s : list) {
                System.out.println(s);
            }
        }
    }
    

4. Function接口

  • java.util.function.Function<T,R>接口:用来根据一个类型的数据得到另一个类型的数据,前者称为前置条件,后者称为后置条件。
  • 抽象方法:R apply(T t),根据类型T的参数获取类型R的结果。
//将String类型转换为Integer类型
import java.util.function.Function;

public class Test01 {
    public static void change(String str, Function<String, Integer> func) {
        Integer in = func.apply(str);
        System.out.println(in + 10);     // 30
    }

    public static void main(String[] args) {
        String s = "20";
        change(s, (String str)->{
            return Integer.parseInt(s);
        });
    }
}
  • 默认方法:andThen用来进行组合操作。
/*
	把String类型的“123”,转换为Integer类型,把转换的结果加10;
	把增加之后的Integer类型的数据,转换为String类型
*/
import java.util.function.Function;

public class Test02 {
    public static void change(String str, Function<String,Integer> func1, Function<Integer,String> func2){
        String s = func1.andThen(func2).apply(str);
        System.out.println(s);   // "133"
    }

    public static void main(String[] args) {
        String str = "123";
        change(str,(String s)->{
            return Integer.parseInt(s) + 10;
        },(Integer i)->{
            return i + "";
        });
    }
}
  • 练习:自定义函数模型拼接
/*
	请使用Function进行函数模型的拼接,按照顺序需要执行的多个函数操作为:
		String str = "朱古力,22";
  1.将字符串截取数字部分,得到字符串;
  2.将上一步的字符串转换为int类型的数字;
  3.将上一步的int数字累加100,得到结果int数字。
*/
import java.util.function.Function;

public class Test {
    public static void change(String str, Function<String,String> func1,
                              Function<String,Integer> func2,
                              Function<Integer,Integer> func3 ) {
        int i = func1.andThen(func2).andThen(func3).apply(str);
        System.out.println(i);
    }

    public static void main(String[] args) {
        String str = "朱古力,22";
        change(str,(String s)->{
            return s.split(",")[1];
        },(String s)->{
            return Integer.parseInt(s);
        },(Integer i)->{
            return i + 100;
        });
    }
}

转载:https://blog.csdn.net/weixin_44781238/article/details/106135657
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场